完美整数 - MarsCode
问题描述
一个整数如果由相同的数字构成,则称为完美整数。例如:
1、11、333是完美整数。12、19、101是不完美整数。
现在,你需要计算给定区间 [x, y] 中有多少个整数是完美整数。
思路解析1-暴力判断
- 定义完美整数:一个整数如果由相同的数字构成,则称为完美整数。例如,
111、2222是完美整数,而123、101不是。 - 判断一个数是否为完美整数
- 将整数转换为字符串。
- 检查字符串中的所有字符是否相同。
- 遍历区间[x, y]
- 从
x到y遍历每个整数。 - 对每个整数判断其是否为完美整数。
- 统计完美整数的数量。
- 从
代码实现
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)
以上代码暴力答案超时(同样的代码有的人能过),可能是数据范围刚好卡点了。
时间复杂度
遍历区间长度(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 中通常会利用常规计数问题技巧:比如把一个区间内的答案拆成两部分相减