如果一个正整数自身是回文数,而且它也是一个回文数的平方,那么我们称这个数为超级回文数。
现在,给定两个正整数 L 和 R (以字符串形式表示),返回包含在范围 [L, R] 中的超级回文数的数目。
输入:L = "4", R = "1000"
输出:4
解释:
4,9,121,以及 484 是超级回文数。
注意 676 不是一个超级回文数: 26 * 26 = 676,但是 26 不是回文数。
提示:
1 <= len(L) <= 18
1 <= len(R) <= 18
L 和 R 是表示 [1, 10^18) 范围的整数的字符串。
int(L) <= int(R)
对从[L,R] 内进行遍历,判断是否是回文数且其开平方后的数也是回文数。这种方法遍历的数过于大,会导致超时。
为减少遍历所需要的数,可以对[L,R]中每一个回文数进行遍历,判断其平方是否为回文数。
对于如何构造回文数,可以观察回文数的规律
位数为1的回文数:
1 2 3 4 5 6 7 8 ....... 9
位数为2
11 22 33 44 ........ 99
位数为3
101 111 121 ...... 202 212 ....... 999
可以得知在位数相同时,回文数的最外层数字可以为 1-9 内层为 0-9
而且回文数递增的规律是外层数字相同下,根据内层数字大小递增。外层元素越大,回文数越大,因此可以采用递归形式,先构造1-9的最外层元素,再构造内层0-9的内层元素
代码:
public int superpalindromesInRange(String L, String R) {
long leftVaule = Long.valueOf(L);
long rightVaule = Long.valueOf(R);
long left = (long)Math.sqrt(leftVaule);
long right = (long)Math.sqrt(rightVaule);
int start = String.valueOf(left).length();
int end = String.valueOf(right).length();
int result = 0;
//从开始范围的回文数位数开始遍历
for(int i=start;i<=18;++i){
ArrayList<Long> list = new ArrayList<>();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.setLength(i);
dfs(0,i-1,stringBuilder,list,true);
for(long k : list){
long tmp = k*k;
if(tmp>=leftVaule&&tmp<=rightVaule&&isReverse(tmp))
++result;
else if(tmp>rightVaule)
return result;
}
}
return result;
}
//判断是否为回文数
public boolean isReverse(Long l){
long tmp = l;
long ans = 0;
while (l>0){
ans = ans*10 + l%10;
l = l/10;
}
return tmp==ans;
}
//构造回文数 从外层开始
public void dfs(int start,int end,StringBuilder string,ArrayList<Long> list,boolean op){
//当递归到末尾时,回文数构造完成,添加进行list中
if(start>end) {
list.add(Long.valueOf(string.toString()));
return;
}
if(op) {
//最外层从0-9构造
for (char i = '1'; i <= '9'; ++i) {
string.setCharAt(start, i);
string.setCharAt(end, i);
dfs(start + 1, end - 1, string, list, false);
}
}
else {
//内层从1-9开始构造
for (char i = '0'; i <= '9'; ++i) {
string.setCharAt(start, i);
string.setCharAt(end, i);
dfs(start + 1, end - 1, string, list, op);
}
}
}