完美整数-算法题解 | 豆包MarsCode AI刷题

151 阅读5分钟

问题描述

一个整数如果由相同数字构成,可以称为完美整数;比如说1、11、333就是完美整数,12、19、101就是不完美的整数。 现在想知道,在区间 [x, y] 中有多少个整数是完美整数。 ## 输入格式 每个样例有一行,是整数 xy;(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)的时间内取得答案,相较于暴力循环效率还是很高的 如有错误欢迎在评论区指正