Loading

leetcode233. 数字 1 的个数

题目链接:https://leetcode-cn.com/problems/number-of-digit-one/
萌新刚开始刷题,啥也不会,只会暴力,这题n这么大,肯定不能暴力了,遂直接看题解。。。

原来可以枚举每一个数位,分别统计每个数位上1出现的次数,再把所有数位上的统计结果相加,就是最终的结果。

举个例子:
n = 123456
个位,从[0]到[9],数字1在个位上出现的次数为1,即[1],个位上每经过一个0-9的循环,个位上1出现的次数就+1。
十位,从[00]到[99],数字1在十位上出现的次数为10,即[10,19],每经过一个0-99的循环,十位上1出现的次数就+10。
百位,从[000]到[999],数字1在百位上出现的次数为100,即[100,199],每经过一个0-999的循环,百位上数字1出现的次数就+100。

现在以统计n百位上数字1出现的次数来得出通式,实际上是有两部分,一部分是看n有多少个[000,999]的循环,从0开始每1000个数,就会经过一次这样的循环,那么就需要知道n有多少个1000,用n/1000向下取整,就是完整循环的次数,再用次数乘100就是这部分1出现的次数,通式为\([n/1000]*100\),其中[]代表向下取整,另一部分是对于n/1000舍去的部分即n%1000,需要分类讨论,记\(n'=n\%1000\)

  • \(n' < 100\)时,百位不会出现数字1
  • \(100 ≤ n'< 200\)时,百位上出现1的范围为\([100,n']\),出现次数为\(n'-100+1\)
  • \(n' ≥ 200\)时,百位上出现1的次数为100

n' < 100时,n'-100+1的结果小于等于0,此时我们需要的结果是0
n' ≥ 200时,n'-100+1的结果大于100,此时我们需要的结果是100
通过总结归纳,可以得出这一部分百位上数字1出现的次数为 \(min(max(n'-100 + 1,0), 100)\)

将这两部分结果相加就是百位上1出现的次数了

\([n/1000]*100 + min(max(n\%1000-100 + 1, 0), 100)\)

用类似的方法可以得到其他位上数字1出现的次数,用\(10^k\)表示数位,上述通式可改写为
\([n/10^{k+1}]*10^k + min(max(n\%10^{k+1}-10^k + 1, 0),10^k)\)

解题方法:
从小达到枚举k,如果\(n>10^k\),证明n包含\(10^k\)对应的数位,通过上式计算这一位数上1出现的次数并累加。

C++代码:

class Solution {
public:
    int countDigitOne(int n) {
        // mulk 表示 10^k
        // 在下面的代码中,可以发现 k 并没有被直接使用到(都是使用 10^k)
        // 但为了让代码看起来更加直观,这里保留了 k
        long long mulk = 1;
        int ans = 0;
        for (int k = 0; n >= mulk; ++k) {
            ans += (n / (mulk * 10)) * mulk + min(max(n % (mulk * 10) - mulk + 1, 0LL), mulk);
            mulk *= 10;
        }
        return ans;
    }
};
posted @ 2021-08-13 11:55  泠枫Jun  阅读(59)  评论(0编辑  收藏  举报