问题: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; } }