0233. 数字 1 的个数
大约 3 分钟
0233. 数字 1 的个数
- 标签:递归、数学、动态规划
- 难度:困难
题目链接
题目大意
描述:给定一个整数 。
要求:计算所有小于等于 的非负整数中数字 出现的个数。
说明:
- 。
示例:
- 示例 1:
输入:n = 13
输出:6
- 示例 2:
输入:n = 0
输出:0
解题思路
思路 1:动态规划 + 数位 DP
将 转换为字符串 ,定义递归函数 def dfs(pos, cnt, isLimit):
表示构造第 位及之后所有数位中数字 出现的个数。接下来按照如下步骤进行递归。
- 从
dfs(0, 0, True)
开始递归。dfs(0, 0, True)
表示:- 从位置 开始构造。
- 初始数字 出现的个数为 。
- 开始时受到数字 对应最高位数位的约束。
- 如果遇到 ,表示到达数位末尾,此时:返回数字 出现的个数 。
- 如果 ,则定义方案数 ,令其等于 ,即:
ans = 0
。 - 如果遇到 ,说明之前位数没有填写数字,当前位可以跳过,这种情况下方案数等于 位置上没有受到 位的约束,并且之前没有填写数字时的方案数,即:
ans = dfs(i + 1, state, False, False)
。 - 如果 ,则当前位必须填写一个数字。此时:
- 因为不需要考虑前导 所以当前位数位所能选择的最小数字()为 。
- 根据 来决定填当前位数位所能选择的最大数字()。
- 然后根据 来枚举能够填入的数字 。
- 方案数累加上当前位选择 之后的方案数,即:
ans += dfs(pos + 1, cnt + (d == 1), isLimit and d == maxX)
。cnt + (d == 1)
表示之前数字 出现的个数加上当前位为数字 的个数。isLimit and d == maxX
表示 位受到之前位 位限制。
- 最后的方案数为
dfs(0, 0, True)
,将其返回即可。
思路 1:代码
class Solution:
def countDigitOne(self, n: int) -> int:
# 将 n 转换为字符串 s
s = str(n)
@cache
# pos: 第 pos 个数位
# cnt: 之前数字 1 出现的个数。
# isLimit: 表示是否受到选择限制。如果为真,则第 pos 位填入数字最多为 s[pos];如果为假,则最大可为 9。
def dfs(pos, cnt, isLimit):
if pos == len(s):
return cnt
ans = 0
# 不需要考虑前导 0,则最小可选择数字为 0
minX = 0
# 如果受到选择限制,则最大可选择数字为 s[pos],否则最大可选择数字为 9。
maxX = int(s[pos]) if isLimit else 9
# 枚举可选择的数字
for d in range(minX, maxX + 1):
ans += dfs(pos + 1, cnt + (d == 1), isLimit and d == maxX)
return ans
return dfs(0, 0, True)
思路 1:复杂度分析
- 时间复杂度:。
- 空间复杂度:。