如何在Python中以更低时间复杂度求解两数间所有数字的各位之和?
你的代码在小范围测试用例上能正常工作,但当处理跨度接近1e9的数值范围时,直接遍历每个数字再拆分各位的做法会因为时间复杂度太高(O((b-a+1)*位数))而超时——毕竟要循环1e9次,这在任何编程竞赛的时间限制里都是不可能完成的。
解决这个问题的核心思路是用数学方法直接计算从0到n的所有数字的各位之和,然后通过sum_up_to(b) - sum_up_to(a-1)得到a到b之间的结果。这种方法的时间复杂度只和数字的位数有关(最多10次循环,因为1e9是10位数),完全能应对题目中的最大约束。
具体实现思路
我们逐位处理数字n的每一位(个位、十位、百位……),计算每一位在0到n的所有数字中贡献的总和:
- 把当前位对应的数字拆成三个部分:
high:当前位左边的所有数字curr:当前位的数字low:当前位右边的所有数字pow10:当前位的位权(比如十位的位权是10)
- 先计算高位从0到
high-1时的贡献:此时当前位可以取0-9的所有数字,每个数字出现pow10次,总和是high * 45 * pow10(45是0到9的数字和) - 再计算高位等于
high时的贡献:- 如果
curr > 0:先算当前位取0到curr-1的总和,再算当前位取curr时的总和(共出现low+1次) - 如果
curr == 0:当前位只能取0,贡献为0,无需额外计算
- 如果
完整代码
def sum_digits_up_to(n): if n < 0: return 0 total = 0 pow10 = 1 while pow10 <= n: divisor = pow10 * 10 high = n // divisor curr = (n // pow10) % 10 low = n % pow10 # 高位从0到high-1时的贡献 total += high * 45 * pow10 # 高位等于high时的贡献 if curr > 0: # 当前位取0到curr-1的总和 total += (curr * (curr - 1) // 2) * pow10 # 当前位取curr的总和 total += curr * (low + 1) pow10 *= 10 return total # 处理输入:一行输入两个数字 a, b = map(int, input().split()) result = sum_digits_up_to(b) - sum_digits_up_to(a - 1) print(result)
关键说明
- 输入处理:原代码用了两次
input(),但题目要求是依次输入两个数字(通常是一行输入),所以改用map(int, input().split())更符合题目要求,也避免了输入格式错误 - 边界处理:当
a=0时,sum_digits_up_to(a-1)即sum_digits_up_to(-1)会返回0,结果就是sum_digits_up_to(b),完全正确 - 测试示例:输入
8 13时,sum_digits_up_to(13)=55,sum_digits_up_to(7)=28,55-28=27,和示例输出一致
这种方法不管数值多大,都能在极短时间内完成计算,轻松通过所有测试用例。
内容的提问来源于stack exchange,提问作者Jana M




