博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
由两个n位数相乘得到的最大回文 Largest Palindrome Product
阅读量:6291 次
发布时间:2019-06-22

本文共 3498 字,大约阅读时间需要 11 分钟。

  hot3.png

问题:

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.

Example:

Input: 2

Output: 987

Explanation: 99 x 91 = 9009, 9009 % 1337 = 987

Note:

The range of n is [1,8].

解决:

【题意】找到由两个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[]>() {
           
            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;
    }
}

转载于:https://my.oschina.net/liyurong/blog/1603314

你可能感兴趣的文章
(转)第三方支付参与者
查看>>
程序员修炼之道读后感2
查看>>
DWR实现服务器向客户端推送消息
查看>>
js中forEach的用法
查看>>
Docker之功能汇总
查看>>
!!a标签和button按钮只允许点击一次,防止重复提交
查看>>
(轉貼) Eclipse + CDT + MinGW 安裝方法 (C/C++) (gcc) (g++) (OS) (Windows)
查看>>
还原数据库
查看>>
作业调度框架 Quartz.NET 2.0 beta 发布
查看>>
mysql性能的检查和调优方法
查看>>
项目管理中的导向性
查看>>
Android WebView 学习
查看>>
(转)从给定的文本中,查找其中最长的重复子字符串的问题
查看>>
HDU 2159
查看>>
spring batch中用到的表
查看>>
资源文件夹res/raw和assets的使用
查看>>
UINode扩展
查看>>
LINUX常用命令
查看>>
百度云盘demo
查看>>
概率论与数理统计习题
查看>>