2020-05-17 09:03:13
问题描述:
给定正整数 N,返回小于等于 N 且具有至少 1 位重复数字的正整数的个数。
示例 1:
输入:20
输出:1
解释:具有至少 1 位重复数字的正数(<= 20)只有 11 。
示例 2:
输入:100
输出:10
解释:具有至少 1 位重复数字的正数(<= 100)有 11,22,33,44,55,66,77,88,99 和 100 。
示例 3:
输入:1000
输出:262
提示:
1 <= N <= 10^9
问题求解:
经典的数位dp题,需要注意的是leading zero不能算为使用过的数字即可。
int[] digits = new int[64]; // pos,limit, used,f,lead int[][][][][] dp = new int[12][2][1 << 10][2][2]; public int numDupDigitsAtMostN(int N) { int pos = 0; while (N > 0) { digits[pos++] = N % 10; N /= 10; } //for (int i = pos - 1; i >= 0; i--) System.out.println(digits[i]); for (int i = 0; i < 12; i++) { for (int j = 0; j < 2; j++) { for (int k = 0; k < 1 << 10; k++) { for (int t = 0; t < 2; t++) { Arrays.fill(dp[i][j][k][t], -1); } } } } return dfs(pos - 1, 1, 0, 0, 1); } private int dfs(int pos, int limit, int used, int f, int lead) { if (pos == -1) return f == 1 ? 1 : 0; if (dp[pos][limit][used][f][lead] != -1) return dp[pos][limit][used][f][lead]; dp[pos][limit][used][f][lead] = 0; int up = limit == 1 ? digits[pos] : 9; for (int i = 0; i <= up; i++) { int ns = used; if (!(lead == 1 && i == 0)) ns = ns | (1 << i); int flag = 0; if (((1 << i) & used) != 0) { if (i == 0 && lead == 1) flag = 0; else flag = 1; } dp[pos][limit][used][f][lead] += dfs(pos - 1, limit == 1 && i == up ? 1 : 0, ns, f == 1 || flag == 1 ? 1 : 0, lead == 1 && i == 0 ? 1 : 0); } return dp[pos][limit][used][f][lead]; }