由两个n位数相乘获得的最大回文 Largest Palindrome Product

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

相关文章
相关标签/搜索