【困难】最大回文数乘积

题目描述

给定一个整数 n ,返回 可表示为两个 n 位整数乘积的 最大回文整数 。因为答案可能非常大,所以返回它对 1337 取余 。

示例 1:
输入:n = 2
输出:987
解释:99 x 91 = 9009, 9009 % 1337 = 987

示例 2:
输入: n = 1
输出: 9

提示:
1 <= n <= 8

解题思路

首先说一下我自己的思路(很蠢的思路。。。最后居然超时了)
这个题我一开始就剑走偏锋了,因为我觉得java中的常见类型显然无法保存16位的数(最大的回文数),然而实际上==>(long是8个字节, 2 63 = 9 ∗ 1 0 18 2^{63}=9*10^{18} 263=91018)。其实我知道java中long类型是8个字节,也知道它最大可以表示 2 63 2^{63} 263,但是 2 63 2^{63} 263具体有多大,我没有深刻的印象。所以,导致我写了半天代码,用了一种愚蠢的解法,不过这种解法是可以成功算出结果的,在IDEA中,n=6,7,8时,需要等3-7秒才会算完,显然,提交时超时了。

简单说一下我的思路,我认为16位的数无法用常规的类型保存,所以用字符串表示每一个回文数,两数相除时依次从字符串中取每一位,并把余数保存在临时变量中,当取到字符串最后一位时,如果余数为0,那就可以除尽。此时只需判断商和除数的位数是否都等于n即可。但是,由于将数字保存在字符串中,很多简单的运算(例如加减乘除取余)都需要我自己写函数,这更是增添了诸多无谓的耗时。当我看题解发现用一个long就可以很漂亮的解决这个问题时,我真的觉得我太蠢了。

2 31 = 2 ∗ 1 0 9 2^{31}=2*10^9 231=2109 2 63 = 9 ∗ 1 0 18 2^{63}=9*10^{18} 263=91018,这下记忆深刻了。

我的实现代码如下:

import java.math.BigDecimal;
import java.math.BigInteger;

class Solution {
    int hui;
    int ji;
    public int largestPalindrome(int n) {
        String s=inits(n);
        s=trans(s);
        while(true){
            for(int div=(int)(Math.pow(10,n))-1;div>(s.length()==1?0:Integer.parseInt(s.substring(0,(s.length()+1)/2)));div--){
                if(isDiv(s,div,n))
                    return quyu(s);
            }
            s=nextHui(n);
            if(s.length()<2*n-1) break;
        }
        return -1;
    }
    public int quyu(String s){
        int div=1337;
        int tmp=0,yu=0;
        for(int i=0;i<s.length();i++){
            tmp=Integer.parseInt(s.substring(i,i+1))+yu*10;
            yu=tmp%div;
        }
        return yu;
    }
    public String nextHui(int n){
        int k=0,tmp=hui;
        while(tmp>0){
            if(tmp%10!=0 && tmp!=1){
                k=-1;
                break;
            }
            tmp=tmp/10;
        }
        if(k==0 && ji==0){
            ji=1;
            hui=hui*10-1;
        }
        else if(k==0 && ji==1){
            ji=0;
            hui-=1;
        }
        else{
            hui-=1;
        }
        return trans(String.valueOf(hui));
    }
    public String inits(int n){
        ji=0;
        hui=0;
        for(int i=0;i<n;i++){
            hui=hui*10+9;
        }
        return String.valueOf(hui);
    }
    public String trans(String s){
        String s0=s;
        for(int i=ji==0?s.length()-1:s.length()-2;i>=0;i--){
            s0+=s.substring(i,i+1);
        }
        return s0;
    }
    public boolean isDiv(String s,int div,int n){
        int tmp=0,yu=0;
        int shou=-1;
        for(int i=0;i<s.length();i++){
            tmp=Integer.parseInt(s.substring(i,i+1))+yu*10;
            yu=tmp%div;
            if(shou==-1 && tmp/div!=0) {
                shou = s.length() - i;
                if(shou!=n) return false;
            }
            if(i==s.length()-1 && yu==0 && shou==n){
                return true;
            }
        }
        return false;
    }
    public static void main(String[] args){
        Solution s= new Solution();
        String s0="56789";
        for(int i=1;i<9;i++)
            System.out.println(s.largestPalindrome(i));
//        System.out.println(s.largestPalindrome(6));
    }
}

官方解法中没有考虑奇数位数的回文数,例如n=3,当回文数左半边从999遍历到100时,回文数也从999999遍历到了100001,但下一个回文数左半边为99(对应回文数为9999),所有奇数位的回文数是缺失的。当然,这个题之所以能解出来,是因为每个满足要求的最大回文数都离(int) Math.pow(10, n) - 1非常近,回文数左半边还来不及遍历到(int) Math.pow(10, n-1)就已经return了,但是我觉得这种写法是不应该的,因为你在写代码之前是不应该事先知道它绝对不会遍历到(int) Math.pow(10, n-1),如果换一种情况,它会遍历到(int) Math.pow(10, n-1)呢?这时一定会出现致命的错误。

这个题出的并不好,显然出题人并没有考虑到我上面所说的这种情况,不然也不会给出这种解法。

class Solution {
    public int largestPalindrome(int n) {
        if (n == 1) {
            return 9;
        }
        int upper = (int) Math.pow(10, n) - 1;
        int ans = 0;
        for (int left = upper; ans == 0; --left) { // 枚举回文数的左半部分
            long p = left;
            for (int x = left; x > 0; x /= 10) {
                p = p * 10 + x % 10; // 翻转左半部分到其自身末尾,构造回文数 p
            }
            for (long x = upper; x * x >= p; --x) {
                if (p % x == 0) { // x 是 p 的因子
                    ans = (int) (p % 1337);
                    break;
                }
            }
        }
        return ans;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冷冰殇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值