问题描述
一个整数如果由相同数字构成,可以称为完美整数;比如说1、11、333就是完美整数,12、19、101就是不完美的整数。
现在想知道,在区间 [x, y] 中有多少个整数是完美整数。 ## 输入格式
每个样例有一行,是整数 x 和 y;(1 ≤ x ≤ y ≤ 10^9)
思路
这题算是简单题目,直接一个循环暴力就可以搞定,可是一看范围如果暴力铁定是浪费时间,非常耗时间,所以我又想了一个O(1)的算法来解决
根据题意,必须所有位上的数字全都一样才算是完美整数,可以大概枚举一下符合题意的数字
0-10 1 2 3 4 5 6 7 8 9
10-100 11 22 33 44 55 66 77 88 99
100-1000 111 222 333 444 555 666 777 888 999
..............................................................
可以发现,对于每个n位的数字,最多有九个完美整数,那么我们直接对边界x、y进行判断即可,中间跨过的n位数字直接乘以9加到答案上就可以出结果,这题只需要求出个数而不需要具体的解,所以这种方法可行,对于边界的讨论分很多情况,我也是整理了半天才写出来 首先将边界转为字符串,计算x、y的位数差 diff=x.length()-y.length()
如果左右边界位数不同
我们分开计算左边界和右边界的情况,然后中间跨过多少位数diff-1直接乘以9即可 对于边界,我们取它的最高位数字k,判断它和最高位数字对应的完美整数的关系
对于左边界对应的n位数
如果左边界大于对应的完美整数,那么数字k对应的完美整数就取不到了,我们可以获取的完美整数有 9-k 个,例如数字56,两位数中能取到的有66、77、88、99
如果左边界小于对应的完美整数,那么数字k对应的完美整数可以取到,总共可以获取 9-k+1 个,例如数字52,两位数中能取到的有55、66、77、88、99
对于右边界对应的n位数
将右边界最高位上的数字赋值给k,如果右边界大于等于对应的完美整数,那么数字k对应的完美整数可以取到,可以获取的完美整数有 k-0即k 个,例如数字56,可以取到11、22、33、44、55
如果右边界小于对应的完美整数,那么数字k对应的完美整数就取不到了,可以获取的完美整数有k-1个,例如数字52,能取到的有11、22、33、44
处理中间跨过的位数
前面分析了,对于每个位数的数字最多就有9个完美整数,例如左边界是3位数,右边界是5位数,那么中间的4位数的全部完美整数都可以取到,所以此时在加上 (diff-1)*9个完美整数即可
举个例子,假设左边界为25 ,右边界为2225 按照上面的方法分别计算2位数和4位数可以取到的完美整数
两位数中,由于25大于22,只能往后取 33、44、55、66、77、88、99,一共9-2=7个
四位数中,由于2225大于2222,往前取 1111、2222 ,一共2-0=2个
中间完整跨过三位数,三位数中可以取到的所有数111、222、333、444、555、666、777、888、999一共(4-2-1)*9=9个,下面是这些情况的代码 ` if (diff > 0) {
//左边界大于对应的完美整数
if (isBigger(leftnum)) {
ans += '9' - k;
} else {
ans += '9' - k + 1;
}
k = rightnum.charAt(0);
//右边界大于等于对应的完美整数
if ( isBigger(rightnum)||isEqual(rightnum)) {
ans += k - '0';
} else {
ans += k - '0'-1;
}
//计算被完整跨过的位数的完美整数
ans += (diff - 1) * 9;
}
`
左右边界位数相同
k为左边界最高位数字,kk为右边界最高位数字
如果k大于k对应的完美整数
k对应的完美整数无法取到
若kk大于等于kk对应的完美整数,那么可以取到的完美整数有kk-k个,例如23 27,获取0个,23 34,可以取到33一个 即kk-k个
若kk小于kk对应的完美整数,那么kk对应的完美整数无法取到,注意右边界是要比左边界大的,所以此时kk一定大于k,我们可以取到的完美整数有kk-k-1个,例23 32,我们可以取到零个
如果k小于等于k对应的完美整数
那么k对应的完美整数是可以取到的
若kk大于等于kk对应的完美整数,那么可以取到的完美整数有kk-k+1个,例21 25,可以取到1个
若kk小于kk对应的完美整数,那么可以取到的完美整数为kk-k个,例20 21,可以取到0个
举个例子,左边界为124 右边界为666,它们位数相同
左边界大于对应的完美整数,同时右边界大于等于对应的完美整数,结果为6-1=5个,可以列举出来为 222、333、444、555、666
`else {//位数相同
char kk = rightnum.charAt(0);
// 左边界数字大于完美整数
if (isBigger(leftnum) ) {
// 右边界数字大于等于完美整数
if (isBigger(rightnum)||isEqual(rightnum)) {
ans += kk - k;
} else {
ans += kk - k -1;
}
} else {// 左边界数字小于等于完美整数
// 右边界数字大于等于完美整数
if (isBigger(rightnum)||isEqual(rightnum)){
ans += kk - k +1;
} else {
ans += kk - k;
}
}
}`
以上分情况比较啰嗦,但是经过不断的调整可以在O(1)的时间内取得答案,相较于暴力循环效率还是很高的 如有错误欢迎在评论区指正