📜  门| Gate IT 2007 |问题10(1)

📅  最后修改于: 2023-12-03 15:12:39.057000             🧑  作者: Mango

问题10

题目描述

给定一个正整数 $n$,请写出一个函数来计算小于等于 $n$ 的非负整数中数字 1 出现的次数。

例如,给定 $n = 13$,则返回 $6$,因为数字 $1$ 出现在以下数字中:$1, 10, 11, 12, 13$。

解题思路

这道题我们可以从逐位分析的角度入手。对于一个数 $m$,我们可以分别分析它的个位、十位、百位......等等上出现数字 $1$ 的个数。为了简化分析,我们设当前分析的位数为 $i$。

假设当前位为 $0$
  • 如果当前位上数字为 $0$,那么它在所有小于等于 $m$ 的整数中出现的次数为 $0$。在下一位的分析中此步骤仍然适用。
假设当前位为 $1$
  • 如果当前位数字为 $1$,则当前位上数字为 $1$ 的情况共有两种:
  1. 低位从 $0 \sim (i-1)$,这样当前位上数字为 $1$ 的数字有 $x$ 个,其中 $x$ 为低 $i-1$ 位的数字取值个数。特别的,如果低位全部为 $0$,那么只有一个数字,即当前的 $m$。
  2. 低位从 $i \sim k-1$,这样当前位上数字为 $1$ 的数字也有 $x$ 个,其中 $x$ 为低 $k-i$ 位的数字取值个数。特别的,如果低位全部为 $0$,那么没有 $1$ 出现。

那么,在 $1\sim m$ 之间,出现 $1$ 的次数就是两种情况下 $1$ 出现的总次数。

假设当前位大于 $1$
  • 如果当前位数字大于 $1$,那么当前位上数字为 $1$ 的情况共有两种:
  1. 低位从 $0\sim (i-1)$,这样当前位上数字为 $1$ 的数字有 $10^{i-1}$ 个,其中 $10^{i-1}$ 为低 $i-1$ 位的数字取值个数。例如,如果当前位所在数是 $2354$,那么在 $1\sim 2354$ 中,第二位上出现 $1$ 的个数为 $10^1=10$。
  2. 低位从 $i\sim k-1$,这样当前位上数字为 $1$ 的数字有 $xxx...xx1$,其中 $xxx...xx$ 为低 $k-i$ 位所有数字的排列组合数,即 $(k-i)$ 位整数中出现数字 $1$ 的次数。例如,如果当前位所在数是 $2416$,那么在 $1\sim 2416$ 中,第三位上出现 $1$ 的个数为 $23 \times 10^1$。

那么,在 $1\sim m$ 之间,出现 $1$ 的次数就是两种情况下 $1$ 出现的总次数。

代码实现
class Solution:
    def countDigitOne(self, n: int) -> int:
        # 特判
        if n <= 0:
            return 0
        
        # 初始化
        x, res = 1, 0
        
        while n >= x:
            # 取当前位
            y = n // x % 10
            
            # 统计出现 1 的次数
            res += (n // (10 * x)) * x + min(max(n % (10 * x) - x + 1, 0), x) if y == 1 else (n // (10 * x) + 1) * x if y > 1 else 0
            
            # 更新位数
            x *= 10
        
        return res
复杂度分析

时间复杂度:$O(\log n)$

空间复杂度:$O(1)$