题目描述
给定一个整数 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=9∗1018)。其实我知道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=2∗109, 2 63 = 9 ∗ 1 0 18 2^{63}=9*10^{18} 263=9∗1018,这下记忆深刻了。
我的实现代码如下:
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;
}
}