AI 刷题 103. 完美整数 题解 | 豆包MarsCode AI刷题

194 阅读5分钟

完美整数 - MarsCode

问题描述

一个整数如果由相同的数字构成,则称为完美整数。例如:

  • 111333 是完美整数。
  • 1219101 是不完美整数。

现在,你需要计算给定区间 [x, y] 中有多少个整数是完美整数。

思路解析1-暴力判断

  1. 定义完美整数:一个整数如果由相同的数字构成,则称为完美整数。例如,1112222 是完美整数,而 123101 不是。
  2. 判断一个数是否为完美整数
    • 将整数转换为字符串。
    • 检查字符串中的所有字符是否相同。
  3. 遍历区间[x, y]
    • xy 遍历每个整数。
    • 对每个整数判断其是否为完美整数。
    • 统计完美整数的数量。

代码实现

def solution(x, y):
    # 初始化完美整数的计数器
    perfect_count = 0
    
    # 遍历区间 [x, y]
    for num in range(x, y + 1):
        # 将整数转换为字符串
        num_str = str(num)
        
        # 检查字符串中的所有字符是否相同
        if all(char == num_str[0] for char in num_str):
            # 如果是完美整数,增加计数器
            perfect_count += 1
    
    # 返回完美整数的数量
    return perfect_count

if __name__ == "__main__":
    # 测试样例
    print(solution(1, 10) == 9)
    print(solution(2, 22) == 10)

以上代码暴力答案超时(同样的代码有的人能过),可能是数据范围刚好卡点了。

image-20241128180327074.png

时间复杂度

遍历区间长度(y-x+1) * (数的位数)

如果区间[x, y] 较大(大概1e8以上)那么会超时。

思路解析2-一点数位dp思想

主要思路:区间[x, y]的完美整数个数 = 区间[1, y]完美整数个数 - 区间[1, x]完美整数个数。

如何求[1, x]的完美整数个数呢

  • 不难看出1-10完美整数有9个、10-100有9个、100-1000有9个
  • 假设x为3位数、首先可以得出1-100有2*9 = 18个。其次如果不想思考了可以"暴力"得出三位数111、222、333...999有几个小于x,当然也可以根据x每位的值判断出3位数中完美数有几个小于x。

代码实现

def solution(x, y):
    """
    函数功能:计算从1到给定整数a之间(包括1和a)的完美整数的个数。
    完美整数定义:一个整数如果由相同的数字构成,则称为完美整数,例如1、11、333等。
    参数:a (int):要计算完美整数个数的上限整数。
    返回值:int:从1到a之间(包括1和a)的完美整数的个数。
    """
    def dp(a):
        # 保存原始输入的整数a,用于后续比较
        original_a = a
        # 用于记录整数a的位数
        digit_count = 0
        # 通过不断除以10,计算整数a的位数
        while a!= 0:
            a //= 10
            digit_count += 1

        # 先初始化完美整数的个数为小于a的最高位之前的各个数位范围的完美整数个数总和
        # 例如对于三位数,先计算1到100之间的完美整数个数,即 (3 - 1) * 9
        perfect_count = (digit_count - 1) * 9

        # 遍历从1到9的数字,用于构造可能的完美整数
        for i in range(1, 10):
            # 临时变量,用于构造当前正在考虑的完美整数
            temp_num = 0
            # 保存当前整数a的位数,用于后续构造完美整数的循环
            current_digit_count = digit_count
            # 通过循环,根据当前遍历到的数字i构造一个与a位数相同的完美整数
            while current_digit_count > 0:
                temp_num = temp_num * 10 + i
                current_digit_count -= 1
            # 如果构造出的完美整数小于等于原始输入的整数a,则完美整数个数加1
            if temp_num <= original_a:
                perfect_count += 1
            # 如果构造出的完美整数已经大于原始输入的整数a,则无需再继续构造更大的完美整数,直接退出循环
            else:
                break
        return perfect_count

    return dp(y) - dp(x-1)

if __name__ == "__main__":
    # 测试样例
    print(solution(1, 10) == 9)
    print(solution(2, 22) == 10)

时间复杂度

max(x, y) 的位数,注意是位数,也可以说是 log(n) 底数为10

几乎常数级别复杂度,小的可怕

补充说明:数位dp

数位是指把一个数字按照个、十、百、千等等一位一位地拆开,关注它每一位上的数字。如果拆的是十进制数,那么每一位数字都是 0~9,其他进制可类比十进制。

数位 DP 是一种用于解决与数字的数位相关的计数问题的算法技巧

一、适用场景

数位 DP 主要适用于这样一类问题:在给定的整数区间内,统计满足特定条件的数字的个数。这些特定条件往往和数字的各位数位上的值有关,比如数位之和为某定值、数位组成的数字满足某种模式等。

二、基本思路

数位 DP 的基本原理是利用计数问题的技巧,例如将区间内的答案拆分为两部分相减,从而避免重复计算。在实现上,可以选择记忆化搜索或循环迭代递推。关键在于如何不重不漏地统计所有不超过上限的答案。这通常涉及从高位到低位枚举每一位数字,考虑每一位可以填入的数字,并利用状态转移数组来统计答案。

数位 DP 中通常会利用常规计数问题技巧:比如把一个区间内的答案拆成两部分相减

ans[l,r]=ans[0,r]ans[0,l]ans[l,r] = ans[0,r] - ans[0, l]