动态规划问题(五)数字1的个数

问题描述

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

​ 例如:对于整数 13,所有包含 1 的非负整数为 13、12、11、10、1 这些数里面数字 1 总共出现了 6 次,因此得到的结果为 6.

解决思路

​ 按照分位数计算 1 出现的次数即可。比如说,对于 13,我们可以首先计算个位数 1 出现的次数,再统计十位数上的出现次数即可。

​ 一般来讲,对于任意的一个整数 n,从个位开始依次计算每个位数 1 出现的次数,最终累加即可。

​ 比如说,对于百分位来讲(如果 n 此时大于 100),那么当前就会有 \(\lfloor \frac {n}{1000} \rfloor\) 个百分位的 1 出现,因此此时就会有 \(\lfloor \frac {n}{1000} \rfloor \times 100\) 个 1 出现(因为 100 本身自带一个 1,因此对于任意的 100,101 .....) 都至少会有一个 1。而对于余数部分,此时要分三种情况:

  • 余数 < 100,那么这种情况下在百分位的 1 就是 0,不要再考虑
  • 100 <= 余数 < 200,此时只有 余数 - 100 + 1 个百分位的 1,+ 1 是由于要包括 100
  • 余数 >= 200,此时就会包含整个百分位的 1,因此这种情况下 1 的个数为 100

因此,对于当前n,再当前的百分位上包含的 1 的个数为:

\(\lfloor \frac {n}{1000} \rfloor \times 100 + min(max(n \mod 1000 - 100 + 1, 0), 100)\)

对于一般的位数:

\(\lfloor \frac {n}{10^{k+1}} \rfloor \times 10^k + min(max(n \mod 10^{k + 1} - 10^k + 1, 0), 10^k)\)

依次遍历每个位数即可得到最终的结果。

实现

public class Solution {
    public static int countDigitOne(int n) {
        int ans     =   0;
        // 初始计算个位数的 1
        int digit   =   1;

        // 不断迭代每个位数的 1,即可得到最终的结果
        while (digit <= n) {
            ans += (n / (digit * 10)) * digit
                    + Math.min(Math.max(n % (digit * 10) - digit + 1, 0), digit);

            digit *= 10;
        }

        return ans;
    }
}
posted @ 2021-08-13 15:57  FatalFlower  阅读(245)  评论(0编辑  收藏  举报