给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。
示例 1:
输入:n = 13
输出:6
示例 2:
输入:n = 0
输出:0
提示:
0 <= n <= 109
思路:
数位DP(不用管前导0)
// 递推型class Solution {public int countDigitOne(int n) {//数位dp//预处理int[] f = new int[10];int[] pow = new int[10];f[0] = 0;pow[0] = 1;for (int i = 1; i < 10; i++) {// 统计低位 和 当前位 1 出现的次数f[i] = f[i - 1] * 10 + pow[i - 1];pow[i] = pow[i - 1] * 10;}List<Integer> nums = new ArrayList<>();while (n > 0) {nums.add(n % 10);n /= 10;}int res = 0, last = 0;for (int i = nums.size() - 1; i >= 0; i--) {int x = nums.get(i);//先统计前面的1可能出现的次数res += last * x * pow[i];if (x == 1) {last++;res += f[i];} else if (x != 0) {res += pow[i] + f[i] * x;}}return res + last;}}
// 记忆化搜索模板class Solution {int[][] f = new int[10][10];List<Integer> num = new ArrayList<>();public int countDigitOne(int n) {while (n > 0) {num.add(n % 10);n /= 10;}for (int i = 0; i < 10; i++)Arrays.fill(f[i], -1);return dfs(num.size() - 1, 1, 0);}int dfs(int cur, int lim, int pre) {if (cur == -1) return pre;int ans = f[cur][pre];if (lim != 1 && ans >= 0) return ans;ans = 0;for (int x = 0; x <= (lim == 1 ? num.get(cur) : 9); x++) {if (lim == 1 && x == num.get(cur))ans += dfs(cur - 1, 1, pre + (x == 1 ? 1 : 0));else ans += dfs(cur - 1, 0, pre + (x == 1 ? 1 : 0));}if (lim != 1) f[cur][pre] = ans;return ans;}}
