Leetcode 1012. Numbers With Repeated Digits
Woodyiiiiiii opened this issue · comments
模版题:数位DP
类似的题目有:
- 1012. Numbers With Repeated Digits
- 233. Number of Digit One
- 面试题 17.06. 2出现的次数
- 600. Non-negative Integers without Consecutive Ones
- 902. Numbers At Most N Given Digit Set
- 2719. Count of Integers (WC 348 Q4)
- 2801. Count Stepping Numbers in Range (WC 356 Q4)
- 2827. Number of Beautiful Integers in the Range (BC 111)
参考资料:
- 数位 DP 通用模板,附题单(Python/Java/C++/Go) By 灵茶山艾府
先决题目条件:
给一个数字n,这个数字范围大到10^9,然后计算0或1到n之间所有数字的位的特性;如果要求在[low, high]的数,就求两次,再用减法
方法:
记忆化搜索。对每位进行DFS,同时为了减少重复计算,用二维数组dp记录每个状态,dp[i][j]表示第i位时当前状态为j(mask或者其他)时的数目。参数same/limit代表是否跟数字n每位相同,循环每位的时候也要考虑最大值。
关键:
因为数字范围是10^9非常大,所以关键是利用条件进行状态压缩,建立缓存,避免重复计算。常见的有:对位压缩mask、对上一个数字压缩、甚至只对数量压缩。
记忆化搜索中缓存的定义与递推中的缓存相比,顺序是反过来的。
答题模版
以No.1012为例
class Solution {
int[][] dp;
public int numDupDigitsAtMostN(int n) {
String str = String.valueOf(n);
int len = str.length();
dp = new int[len][1 << 10];
return n + 1 - dfs(str, 0, 0, true);
}
private int dfs(String str, int pos, int mask, boolean same) {
if (pos == str.length()) {
return 1;
}
if (!same && dp[pos][mask] != 0) {
return dp[pos][mask];
}
int res = 0;
int max = same ? str.charAt(pos) - '0' : 9;
for (int k = 0; k <= max; ++k) {
if ((mask & (1 << k)) != 0) {
continue;
}
res += dfs(str, pos + 1, mask == 0 && k == 0 ? 0 : (mask | (1 << k)), same && k == max);
}
if (!same) {
dp[pos][mask] = res;
}
return res;
}
}
2827. Number of Beautiful Integers in the Range
这次是记录奇偶之差和余数。
做题的时候思路卡在余数这里,虽然看到数据大小有猜想是通过余数实现。但为什么可以**(mod + 10 + i) % k**呢?
class Solution {
public int numberOfBeautifulIntegers(int low, int high, int k) {
int a = cal(high, k), b = cal(low - 1, k);
return a - b;
}
private int cal(int hi, int k) {
String s = String.valueOf(hi);
int n = s.length();
Map<String, Integer> memo = new HashMap<>();
return dfs(0, 0, 0, true, false, n, memo, s, k);
}
private int dfs(int pos, int diff, int mod, boolean isLimit, boolean isNum, final int n, final Map<String, Integer> memo, String s, final int k) {
if (pos == n) {
return (isNum && (diff == 0) && (mod == 0)) ? 1 : 0;
}
String key = pos + "_" + diff + "_" + mod;
if (!isLimit && isNum && memo.containsKey(key)) {
return memo.get(key);
}
int ans = 0;
if (!isNum) {
ans = dfs(pos + 1, diff, mod, false, false, n, memo, s, k);
}
int mx = isLimit ? s.charAt(pos) - '0' : 9;
for (int i = isNum ? 0 : 1; i <= mx; i++) {
ans = ans + dfs(pos + 1, i % 2 == 0 ? diff - 1 : diff + 1, (mod * 10 + i) % k,
isLimit && i == mx, true, n, memo, s, k);
}
if (!isLimit && isNum) {
memo.put(key, ans);
}
return ans;
}
}