LeetCode 题解工作台

数字 1 的个数

给定一个整数 n ,计算所有小于等于 n 的非负整数中数字 1 出现的个数。 示例 1: 输入: n = 13 输出: 6 示例 2: 输入: n = 0 输出: 0 提示: 0 9

category

3

题型

code_blocks

6

代码语言

hub

3

相关题

当前训练重点

困难 · 状态·转移·动态规划

bolt

答案摘要

这道题实际上是求在给定区间 中,数字中出现 个数。个数与数的位数以及每一位上的数字有关。我们可以用数位 DP 的思路来解决这道题。数位 DP 中,数的大小对复杂度的影响很小。 对于区间 问题,我们一般会将其转化为 然后再减去 $[1,..l - 1]$ 的问题,即:

Interview AiBox logo

Interview AiBox 实时 AI 助手,陪你讲清 状态·转移·动态规划 题型思路

试试 AiBox 面试助手arrow_forward
description

题目描述

给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。

 

示例 1:

输入:n = 13
输出:6

示例 2:

输入:n = 0
输出:0

 

提示:

  • 0 <= n <= 109
lightbulb

解题思路

方法一:数位 DP

这道题实际上是求在给定区间 [l,..r][l,..r] 中,数字中出现 11 个数。个数与数的位数以及每一位上的数字有关。我们可以用数位 DP 的思路来解决这道题。数位 DP 中,数的大小对复杂度的影响很小。

对于区间 [l,..r][l,..r] 问题,我们一般会将其转化为 [1,..r][1,..r] 然后再减去 [1,..l1][1,..l - 1] 的问题,即:

ans=i=1ransii=1l1ansians = \sum_{i=1}^{r} ans_i - \sum_{i=1}^{l-1} ans_i

不过对于本题而言,我们只需要求出区间 [1,..r][1,..r] 的值即可。

这里我们用记忆化搜索来实现数位 DP。从起点向下搜索,到最底层得到方案数,一层层向上返回答案并累加,最后从搜索起点得到最终的答案。

基本步骤如下:

我们首先将数字 nn 转化为字符串 ss。然后我们设计一个函数 dfs(i,cnt,limit)\textit{dfs}(i, \textit{cnt}, \textit{limit}),其中:

  • 数字 ii 表示当前搜索到的位置,我们从高位开始搜索,即 i=0i = 0 表示最高位。
  • 数字 cnt\textit{cnt} 表示当前数字中 11 出现的次数。
  • 布尔值 limit\textit{limit} 表示当前是否受到上界的限制。

函数的执行过程如下:

如果 ii 超过了数字 nn 的长度,说明搜索结束,直接返回 cntcnt。如果 limit\textit{limit} 为真,那么 upup 为当前数字的第 ii 位,否则 up=9up = 9。接下来,我们遍历 jj00upup,对于每一个 jj

  • 如果 jj 等于 11,我们将 cntcnt 加一。
  • 递归调用 dfs(i+1,cnt,limitj=up)\textit{dfs}(i + 1, \textit{cnt}, \textit{limit} \land j = up)

答案为 dfs(0,0,True)\textit{dfs}(0, 0, \text{True})

时间复杂度 O(m2×D)O(m^2 \times D),空间复杂度 O(m2)O(m^2)。其中 mm 为数字 nn 的长度,而 D=10D = 10

相似题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution:
    def countDigitOne(self, n: int) -> int:
        @cache
        def dfs(i: int, cnt: int, limit: bool) -> int:
            if i >= len(s):
                return cnt
            up = int(s[i]) if limit else 9
            ans = 0
            for j in range(up + 1):
                ans += dfs(i + 1, cnt + (j == 1), limit and j == up)
            return ans

        s = str(n)
        return dfs(0, 0, True)
speed

复杂度分析

指标
时间O(log_{10}(n))
空间O(1)
psychology

面试官常问的追问

外企场景
  • question_mark

    Checks if candidate recognizes that brute-force counting is too slow and will fail for large n.

  • question_mark

    Wants a solution that properly models each digit's contribution using DP or mathematical counting.

  • question_mark

    Looks for handling of boundary cases like n = 0 or digits with zeros in higher places.

warning

常见陷阱

外企场景
  • error

    Forgetting to handle the current digit correctly when it is 1, leading to undercounting.

  • error

    Using naive iteration through all numbers, which exceeds time limits for large n.

  • error

    Overflowing integer types when summing counts without considering large n.

swap_horiz

进阶变体

外企场景
  • arrow_right_alt

    Count digit 'd' instead of 1 across numbers up to n.

  • arrow_right_alt

    Compute number of times a substring appears in decimal representation of numbers up to n.

  • arrow_right_alt

    Adapt the solution for base-k numbers rather than decimal.

help

常见问题

外企场景

数字 1 的个数题解:状态·转移·动态规划 | LeetCode #233 困难