Woodyiiiiiii / LeetCode

My private record of Leetcode solution

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Leetcode 1012. Numbers With Repeated Digits

Woodyiiiiiii opened this issue · comments

模版题:数位DP

类似的题目有:

  1. 1012. Numbers With Repeated Digits
  2. 233. Number of Digit One
  3. 面试题 17.06. 2出现的次数
  4. 600. Non-negative Integers without Consecutive Ones
  5. 902. Numbers At Most N Given Digit Set
  6. 2719. Count of Integers (WC 348 Q4)
  7. 2801. Count Stepping Numbers in Range (WC 356 Q4)
  8. 2827. Number of Beautiful Integers in the Range (BC 111)

参考资料:

先决题目条件:
给一个数字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;
    }
}