問題:git
Find the largest palindrome made from the product of two n-digit numbers.算法
Since the result could be very large, you should return the largest palindrome mod 1337.ide
Example:測試
Input: 2spa
Output: 987.net
Explanation: 99 x 91 = 9009, 9009 % 1337 = 987code
Note:leetcode
The range of n is [1,8].get
解決:it
【題意】找到由兩個n位數相乘獲得的最大回文。因爲返回的結果可能會很大,因此結果爲乘積 % 1337。n的範圍是[1,8]。https://leetcode.com/problems/largest-palindrome-product/discuss/96306
解決這個問題有兩個方向:一是先構造迴文,而後判斷它是否能夠寫成兩個n位數的乘積; 另外一種是首先得到兩位數字的乘積,而後判斷乘積是不是迴文。
① 先構造迴文,而後判斷它是否能夠寫成兩個n位數的乘積:
首先,須要考慮兩個問題:
1. 如何構建迴文並按降序排列(咱們只須要最大回文數)。
2. 如何判斷一個給定的迴文數能夠寫成兩個數的乘積。
對於第一個問題,咱們須要知道每一個迴文中有多少位數。因爲迴文是兩個n位數的乘積,因此它能夠有2n或2n - 1位數。並且,因爲咱們只須要最大回文數,所以咱們將首先考慮2n位的迴文。這些迴文能夠分爲數字相同的兩部分(每部分n個):左和右。左邊是右邊的鏡像,反之亦然。 所以,每一個迴文將徹底由其左邊或右邊的部分決定。
須要注意的是,左邊的部分是一個n位的數字,若是咱們按照降序排列,最終的迴文也會按降序排列。所以,咱們能夠從最大的n位數向最小的n位數遍歷。對於每一個數字,咱們能夠經過鏈接數字和鏡像來構建迴文。
對於第二個問題,即如何判斷一個給定的迴文數能夠寫成兩個數的乘積。這本質上是「整數分解」問題。一種簡單的方法是「嘗試分割」算法,即測試每一個n位數以查看它是不是迴文數的因子,若是是,則另外一個因子也是n位數字。
注意咱們只考慮了2n位的迴文。 幸運的是,對於每種狀況(n = 1到8),咱們可以找到至少一個能夠分解成兩個n位數字的迴文,所以不須要檢查那些具備2n - 1個數字的迴文。O(10^n)
class Solution { //387ms
public int largestPalindrome(int n) {
if (n == 1) return 9;
long max = (long) Math.pow(10,n) - 1;//n位數的最大回文,上界
long min = max / 10;//下界
for (long p = max;p > min;p --){//從大到小,構造迴文
long left = p;//最大回文的左半部分
long right = 0;
for (long i = p;i != 0;i /= 10){
right = right * 10 + i % 10;
left *= 10;
}
long palindrome = left + right;//構造迴文序列
for (long i = max;i > min;i --){
long j = palindrome / i;
if (j > i || j <= min) break;//若是另外一個因子大於當前因子,或者不是n位數,則終止
if (palindrome % i == 0) return (int)(palindrome % 1337);//若是當前數是一個因子,則找到了知足條件的最大回文
}
}
return 9;//n = 1時
}
}
② 根據上面的分析,咱們只須要關注最大回文數便可,因此能夠從9開始檢查迴文。若是沒找到,則從8,7,6,...開始檢查。若是這個迴文數是以9開始的,則其結尾也應該是9。若是迴文數是兩個n位數的乘積,則這兩個數應該以1,3,7,9結尾。對於其餘的狀況也是如此。
對於每一個n來講,至少存在一個具備2n位的迴文數,它以數字9開始,能夠寫成兩個n位數的乘積。
通過分析,有以下結論:對於每一個n,存在至少兩個n位數字num1和num2,10 ^ n-10 ^ m <= num1,num2 <= 10 ^ n -1和m = [ n + 1)/ 2],其乘積將是迴文數。
先獲得兩個n位數的乘積,再判斷乘積是不是迴文。
與第一種方法相似,咱們須要考慮如下兩個問題:
1. 如何構建乘積並降序排列。
2. 如何判斷給定的乘積是迴文數。
第二個問題很簡單,只需將乘積倒過來,並判斷它是否與原來的乘積相同。 可是,第一個問題有點困難。 得到乘積是一件容易的事。 困難的部分是如何按降序排列。
首先咱們須要肯定候選乘積。 從上面的分析看,咱們只考慮在[10 ^ n - 10 ^ m,10 ^ n - 1]範圍內兩個n位數字得到的乘積。其次,咱們能夠使用PriorityQueue以降序提取這些候選乘積。
然而,當n = 8時,上面候選乘積的總數仍然不少。因此咱們須要進一步的修剪:
首先,因爲乘積以數字9結尾,因此兩個數字必須以數字1,3,7或9結尾。
其次,爲了不重複,第一個數字不會小於第二個。
第三,對於第一個因子固定的全部乘積,沒有必要一次把全部乘積都記下來,而是能夠考慮將第二個因子按順序遞減來遍歷。
最後咱們將這兩個因子存儲在PriorityQueue中,同時根據他們的乘積提取它們。
class Solution {//275ms
public int largestPalindrome(int n) {
//兩個因子的範圍[10^n - 10^m, 10^n - 1],m = [(n+1)/2]
int max = (int) Math.pow(10,n) - 1;
int min = max - (int) Math.pow(10,(n + 1) >> 1);
Comparator<int[]> cmp = new Comparator<int[]>() {
@Override public int compare(int[] o1, int[] o2) {//將乘積按降序排列 return Long.compare((long) o2[0] * o2[1],(long) o1[0] * o1[1]); } }; PriorityQueue<int[]> priorityQueue = new PriorityQueue<>(max - min,cmp); for (int i = max;i > min;i --){ int tail = i % 10; if (tail == 3 || tail == 7){ priorityQueue.offer(new int[]{i,i}); }else if (tail == 1){ priorityQueue.offer(new int[]{i,i - 2}); }else if (tail == 9){ priorityQueue.offer(new int[]{i,i - 8}); } } while (! priorityQueue.isEmpty()){ int[] tmp = priorityQueue.poll(); long palindrome = (long) tmp[0] * tmp[1]; if (isPalindrome(palindrome)){ return (int)(palindrome % 1337); } if (tmp[1] > min){ tmp[1] -= 10; priorityQueue.offer(tmp); } } return 0; } public boolean isPalindrome(long x){ long reverse = 0; for (long tmp = x; tmp != 0;tmp /= 10){ reverse = reverse * 10 + tmp % 10; } return reverse == x; } }