携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第20天,点击查看活动详情
题目
给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。
示例 1
输入:n = 13
输出:6
示例 2
输入:n = 0
输出:0
提示
0 <= n <= 109
题解
思路
先观察以下规律, 9 以下的 1 有 1 个 99 以下的 1 相当于从 10 个 9 里面取 1(个位数),又从 10-19 里面的十位数取 1,所以是 10 + 10*1 = 20 999 以下的有 100 + 10 * 20 = 300 以此类推
那么现在给我们一个 n,我们首先知道它能出现多少次一个小于等于它的 9999... 比如说 3278,必然是包含了 999 的而且 0999,1999,2999 一共相当于出现了三次,这就是我们的 res 部分的 999 中 1 的个数乘以第一位的大小。这个时候没考虑的是什么呢?一个是第一位为 3 时其他位变动产生的 1 以及第一位是 1 的所有 (仅讨论第一位上的1)。
前者正是往后的一个递归了 (比如 3278 我们需要知道 278 能组成多少 1,它是第一位为 3 的 1 的个数),而后者,要讨论是否大于 1,这就好比 3278 中是包含 1000-1999 的所有千位数的 1 的,但是 1278 中只有 279 个千位数的 1。所以我们在返回的时候,判断他是不是大于 1 然后加不同的个数即可。
注意:n 的第一位是不可能为 0 的,这是整数的一个性质,所以这保证了我们第一位要么等于 1,要么大于 1.
代码
class Solution:
def countDigitOne(self, n: int) -> int:
if n < 10:
return 1 if n else 0
num = str(n)
x = len(num) - 1
nxt = int(num[1:])
res = self.f(x) * int(num[0]) + self.countDigitOne(nxt)
# 第一位大于1,包含那位为1的所有1的个数是10**x ; 否则是num后面的部分再加上"10000.."这个情况的1
return res + 10 ** x if int(num[0]) > 1 else res + nxt + 1
"""
0-9: 1
0-99: 10 + 10 * 1 = 20
0-999: 100 + 10 * 20 = 300
0-9999: 1000 + 10 * 300 = 4000
0-99999: 10000 + 10 * 4000 = 50000
f(i) = 10 ** (i-1) + 10 * f(i-1)
其实也可以直接写 f(i) = i * 10 ** (i-1)
"""
@lru_cache(None)
def f(self, i):
# return i * 10 ** (i-1)
return 10 ** (i-1) + 10 * self.f(i-1) if i else 0
结语
业精于勤,荒于嬉;行成于思,毁于随。