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

    示例 1:
    输入:n = 13
    输出:6
    示例 2:
    输入:n = 0
    输出:0

    提示:
    0 <= n <= 109

    思路:
    数位DP(不用管前导0)

    1. // 递推型
    2. class Solution {
    3. public int countDigitOne(int n) {
    4. //数位dp
    5. //预处理
    6. int[] f = new int[10];
    7. int[] pow = new int[10];
    8. f[0] = 0;
    9. pow[0] = 1;
    10. for (int i = 1; i < 10; i++) {
    11. // 统计低位 和 当前位 1 出现的次数
    12. f[i] = f[i - 1] * 10 + pow[i - 1];
    13. pow[i] = pow[i - 1] * 10;
    14. }
    15. List<Integer> nums = new ArrayList<>();
    16. while (n > 0) {
    17. nums.add(n % 10);
    18. n /= 10;
    19. }
    20. int res = 0, last = 0;
    21. for (int i = nums.size() - 1; i >= 0; i--) {
    22. int x = nums.get(i);
    23. //先统计前面的1可能出现的次数
    24. res += last * x * pow[i];
    25. if (x == 1) {
    26. last++;
    27. res += f[i];
    28. } else if (x != 0) {
    29. res += pow[i] + f[i] * x;
    30. }
    31. }
    32. return res + last;
    33. }
    34. }
    1. // 记忆化搜索模板
    2. class Solution {
    3. int[][] f = new int[10][10];
    4. List<Integer> num = new ArrayList<>();
    5. public int countDigitOne(int n) {
    6. while (n > 0) {
    7. num.add(n % 10);
    8. n /= 10;
    9. }
    10. for (int i = 0; i < 10; i++)
    11. Arrays.fill(f[i], -1);
    12. return dfs(num.size() - 1, 1, 0);
    13. }
    14. int dfs(int cur, int lim, int pre) {
    15. if (cur == -1) return pre;
    16. int ans = f[cur][pre];
    17. if (lim != 1 && ans >= 0) return ans;
    18. ans = 0;
    19. for (int x = 0; x <= (lim == 1 ? num.get(cur) : 9); x++) {
    20. if (lim == 1 && x == num.get(cur))
    21. ans += dfs(cur - 1, 1, pre + (x == 1 ? 1 : 0));
    22. else ans += dfs(cur - 1, 0, pre + (x == 1 ? 1 : 0));
    23. }
    24. if (lim != 1) f[cur][pre] = ans;
    25. return ans;
    26. }
    27. }