diff --git a/solution/0900-0999/0902.Numbers At Most N Given Digit Set/README.md b/solution/0900-0999/0902.Numbers At Most N Given Digit Set/README.md index fe475e4769e5f..1514aaf153ce8 100644 --- a/solution/0900-0999/0902.Numbers At Most N Given Digit Set/README.md +++ b/solution/0900-0999/0902.Numbers At Most N Given Digit Set/README.md @@ -90,18 +90,25 @@ $$ 基本步骤如下: -1. 将数字 $n$ 转为 int 数组 $a$,其中 $a[1]$ 为最低位,而 $a[len]$ 为最高位; -1. 根据题目信息,设计函数 $dfs()$,对于本题,我们定义 $dfs(pos, lead, limit)$,答案为 $dfs(len, 1, true)$。 +我们将数字 $n$ 转化为字符串 $s$,记字符串 $s$ 的长度为 $m$。 -其中: +接下来,我们设计一个函数 $\textit{dfs}(i, \textit{lead}, \textit{limit})$,表示当前处理到字符串的第 $i$ 位,到最后一位的方案数。其中: -- `pos` 表示数字的位数,从末位或者第一位开始,一般根据题目的数字构造性质来选择顺序。对于本题,我们选择从高位开始,因此,`pos` 的初始值为 `len`; -- `lead` 表示当前数字中是否包含前导零,如果包含,则为 `1`,否则为 `0`;初始化为 `1`; -- `limit` 表示可填的数字的限制,如果无限制,那么可以选择 $[0,1,..9]$,否则,只能选择 $[0,..a[pos]]$。如果 `limit` 为 `true` 且已经取到了能取到的最大值,那么下一个 `limit` 同样为 `true`;如果 `limit` 为 `true` 但是还没有取到最大值,或者 `limit` 为 `false`,那么下一个 `limit` 为 `false`。 +- 数字 $i$ 表示当前处理到字符串 $s$ 的第 $i$ 位; +- 布尔值 $\textit{lead}$ 表示是否只包含前导零; +- 布尔值 $\textit{limit}$ 表示当前位置是否受到上界的限制。 -关于函数的实现细节,可以参考下面的代码。 +函数的执行过程如下: -时间复杂度 $O(\log n)$。 +如果 $i$ 大于等于 $m$,说明我们已经处理完了所有的位数,此时如果 $\textit{lead}$ 为真,说明当前的数字是前导零,我们应当返回 $0$;否则,我们应当返回 $1$。 + +否则,我们计算当前位置的上界 $\textit{up}$,如果 $\textit{limit}$ 为真,则 $up$ 为 $s[i]$ 对应的数字,否则 $up$ 为 $9$。 + +然后,我们在 $[0, \textit{up}]$ 的范围内枚举当前位置的数字 $j$,如果 $j$ 为 $0$ 且 $\textit{lead}$ 为真,我们递归计算 $\textit{dfs}(i + 1, \text{true}, \textit{limit} \wedge j = \textit{up})$;否则,如果 $j$ 在 $\textit{digits}$ 中,我们递归计算 $\textit{dfs}(i + 1, \text{false}, \textit{limit} \wedge j = \textit{up})$。累加所有的结果即为答案。 + +最后,我们返回 $\textit{dfs}(0, \text{true}, \text{true})$ 即可。 + +时间复杂度 $O(\log n \times D)$,空间复杂度 $O(\log n)$。其中 $D = 10$。 相似题目: @@ -120,69 +127,59 @@ $$ class Solution: def atMostNGivenDigitSet(self, digits: List[str], n: int) -> int: @cache - def dfs(pos, lead, limit): - if pos <= 0: - return lead == False - up = a[pos] if limit else 9 + def dfs(i: int, lead: int, limit: bool) -> int: + if i >= len(s): + return lead ^ 1 + + up = int(s[i]) if limit else 9 ans = 0 - for i in range(up + 1): - if i == 0 and lead: - ans += dfs(pos - 1, lead, limit and i == up) - elif i in s: - ans += dfs(pos - 1, False, limit and i == up) + for j in range(up + 1): + if j == 0 and lead: + ans += dfs(i + 1, 1, limit and j == up) + elif j in nums: + ans += dfs(i + 1, 0, limit and j == up) return ans - l = 0 - a = [0] * 12 - s = {int(d) for d in digits} - while n: - l += 1 - a[l] = n % 10 - n //= 10 - return dfs(l, True, True) + s = str(n) + nums = {int(x) for x in digits} + return dfs(0, 1, True) ``` #### Java ```java class Solution { - private int[] a = new int[12]; - private int[][] dp = new int[12][2]; - private Set s = new HashSet<>(); + private Set nums = new HashSet<>(); + private char[] s; + private Integer[] f; public int atMostNGivenDigitSet(String[] digits, int n) { - for (var e : dp) { - Arrays.fill(e, -1); + s = String.valueOf(n).toCharArray(); + f = new Integer[s.length]; + for (var x : digits) { + nums.add(Integer.parseInt(x)); } - for (String d : digits) { - s.add(Integer.parseInt(d)); - } - int len = 0; - while (n > 0) { - a[++len] = n % 10; - n /= 10; - } - return dfs(len, 1, true); + return dfs(0, true, true); } - private int dfs(int pos, int lead, boolean limit) { - if (pos <= 0) { - return lead ^ 1; + private int dfs(int i, boolean lead, boolean limit) { + if (i >= s.length) { + return lead ? 0 : 1; } - if (!limit && lead != 1 && dp[pos][lead] != -1) { - return dp[pos][lead]; + if (!lead && !limit && f[i] != null) { + return f[i]; } + int up = limit ? s[i] - '0' : 9; int ans = 0; - int up = limit ? a[pos] : 9; - for (int i = 0; i <= up; ++i) { - if (i == 0 && lead == 1) { - ans += dfs(pos - 1, lead, limit && i == up); - } else if (s.contains(i)) { - ans += dfs(pos - 1, 0, limit && i == up); + for (int j = 0; j <= up; ++j) { + if (j == 0 && lead) { + ans += dfs(i + 1, true, limit && j == up); + } else if (nums.contains(j)) { + ans += dfs(i + 1, false, limit && j == up); } } - if (!limit && lead == 0) { - dp[pos][lead] = ans; + if (!lead && !limit) { + f[i] = ans; } return ans; } @@ -194,43 +191,37 @@ class Solution { ```cpp class Solution { public: - int a[12]; - int dp[12][2]; - unordered_set s; - int atMostNGivenDigitSet(vector& digits, int n) { - memset(dp, -1, sizeof dp); - for (auto& d : digits) { - s.insert(stoi(d)); - } - int len = 0; - while (n) { - a[++len] = n % 10; - n /= 10; + string s = to_string(n); + unordered_set nums; + for (auto& x : digits) { + nums.insert(stoi(x)); } - return dfs(len, 1, true); - } - - int dfs(int pos, int lead, bool limit) { - if (pos <= 0) { - return lead ^ 1; - } - if (!limit && !lead && dp[pos][lead] != -1) { - return dp[pos][lead]; - } - int ans = 0; - int up = limit ? a[pos] : 9; - for (int i = 0; i <= up; ++i) { - if (i == 0 && lead) { - ans += dfs(pos - 1, lead, limit && i == up); - } else if (s.count(i)) { - ans += dfs(pos - 1, 0, limit && i == up); + int m = s.size(); + int f[m]; + memset(f, -1, sizeof(f)); + auto dfs = [&](auto&& dfs, int i, bool lead, bool limit) -> int { + if (i >= m) { + return lead ? 0 : 1; } - } - if (!limit && !lead) { - dp[pos][lead] = ans; - } - return ans; + if (!lead && !limit && f[i] != -1) { + return f[i]; + } + int up = limit ? s[i] - '0' : 9; + int ans = 0; + for (int j = 0; j <= up; ++j) { + if (j == 0 && lead) { + ans += dfs(dfs, i + 1, true, limit && j == up); + } else if (nums.count(j)) { + ans += dfs(dfs, i + 1, false, limit && j == up); + } + } + if (!lead && !limit) { + f[i] = ans; + } + return ans; + }; + return dfs(dfs, 0, true, true); } }; ``` @@ -239,48 +230,79 @@ public: ```go func atMostNGivenDigitSet(digits []string, n int) int { - s := map[int]bool{} - for _, d := range digits { - i, _ := strconv.Atoi(d) - s[i] = true + s := strconv.Itoa(n) + m := len(s) + f := make([]int, m) + for i := range f { + f[i] = -1 } - a := make([]int, 12) - dp := make([][2]int, 12) - for i := range a { - dp[i] = [2]int{-1, -1} - } - l := 0 - for n > 0 { - l++ - a[l] = n % 10 - n /= 10 + nums := map[int]bool{} + for _, d := range digits { + x, _ := strconv.Atoi(d) + nums[x] = true } - var dfs func(int, int, bool) int - dfs = func(pos, lead int, limit bool) int { - if pos <= 0 { - return lead ^ 1 + var dfs func(i int, lead, limit bool) int + dfs = func(i int, lead, limit bool) int { + if i >= m { + if lead { + return 0 + } + return 1 } - if !limit && lead == 0 && dp[pos][lead] != -1 { - return dp[pos][lead] + if !lead && !limit && f[i] != -1 { + return f[i] } up := 9 if limit { - up = a[pos] + up = int(s[i] - '0') } ans := 0 - for i := 0; i <= up; i++ { - if i == 0 && lead == 1 { - ans += dfs(pos-1, lead, limit && i == up) - } else if s[i] { - ans += dfs(pos-1, 0, limit && i == up) + for j := 0; j <= up; j++ { + if j == 0 && lead { + ans += dfs(i+1, true, limit && j == up) + } else if nums[j] { + ans += dfs(i+1, false, limit && j == up) } } - if !limit { - dp[pos][lead] = ans + if !lead && !limit { + f[i] = ans } return ans } - return dfs(l, 1, true) + return dfs(0, true, true) +} +``` + +#### TypeScript + +```ts +function atMostNGivenDigitSet(digits: string[], n: number): number { + const s = n.toString(); + const m = s.length; + const f: number[] = Array(m).fill(-1); + const nums = new Set(digits.map(d => parseInt(d))); + const dfs = (i: number, lead: boolean, limit: boolean): number => { + if (i >= m) { + return lead ? 0 : 1; + } + if (!lead && !limit && f[i] !== -1) { + return f[i]; + } + const up = limit ? +s[i] : 9; + let ans = 0; + for (let j = 0; j <= up; ++j) { + if (!j && lead) { + ans += dfs(i + 1, true, limit && j === up); + } else if (nums.has(j)) { + ans += dfs(i + 1, false, limit && j === up); + } + } + if (!lead && !limit) { + f[i] = ans; + } + return ans; + }; + return dfs(0, true, true); } ``` diff --git a/solution/0900-0999/0902.Numbers At Most N Given Digit Set/README_EN.md b/solution/0900-0999/0902.Numbers At Most N Given Digit Set/README_EN.md index 65c5dc7371fde..256228d5c2851 100644 --- a/solution/0900-0999/0902.Numbers At Most N Given Digit Set/README_EN.md +++ b/solution/0900-0999/0902.Numbers At Most N Given Digit Set/README_EN.md @@ -74,41 +74,48 @@ In total, this is 29523 integers that can be written using the digits array. ### Solution 1: Digit DP -This problem is essentially asking for the count of positive integers generated by the numbers in `digits` within the given range $[l,..r]$. The count is related to the number of digits and the digit at each position. We can use the idea of Digit DP to solve this problem. In Digit DP, the size of the number has a small impact on the complexity. +This problem essentially asks for the number of positive integers that can be generated from the digits in digits within the given range $[l, .., r]$. The count depends on the number of digits and the value of each digit. We can solve this problem using the Digit DP approach. In Digit DP, the size of the number has little impact on the complexity. -For the range $[l,..r]$, we generally convert it into $[1,..r]$ and then subtract $[1,..l - 1]$, i.e., +For the range $[l, .., r]$ problem, we generally convert it to the problem of $[1, .., r]$ and then subtract the result of $[1, .., l - 1]$, i.e., $$ ans = \sum_{i=1}^{r} ans_i - \sum_{i=1}^{l-1} ans_i $$ -However, for this problem, we only need to calculate the value of the range $[1,..r]$. +However, for this problem, we only need to find the value for the range $[1, .., r]$. -Here we use memoization search to implement Digit DP. We search from the starting point to the bottom layer to get the number of schemes, return the answer layer by layer and accumulate it, and finally get the final answer from the starting point of the search. +Here, we use memoization to implement Digit DP. We start searching from the top, get the number of solutions at the bottom, and return the answers layer by layer until we get the final answer from the starting point. The basic steps are as follows: -1. Convert the number $n$ into an int array $a$, where $a[1]$ is the lowest digit and $a[len]$ is the highest digit; -2. Design the function $dfs()$ based on the problem information. For this problem, we define $dfs(pos, lead, limit)$, and the answer is $dfs(len, 1, true)$. +We convert the number $n$ into a string $s$ and denote the length of the string $s$ as $m$. -Where: +Next, we design a function $\textit{dfs}(i, \textit{lead}, \textit{limit})$ to represent the number of solutions from the current $i$-th digit to the last digit of the string. Here: -- `pos` represents the number of digits in the number, starting from the last digit or the first digit, usually chosen based on the digit construction property of the problem. For this problem, we choose to start from the high digit, so the initial value of `pos` is `len`; -- `lead` indicates whether the current number contains leading zeros. If it does, it is `1`, otherwise it is `0`. It is initialized to `1`; -- `limit` represents the restriction on the digits that can be filled. If there is no restriction, you can choose $[0,1,..9]$, otherwise, you can only choose $[0,..a[pos]]$. If `limit` is `true` and the maximum value that can be taken has been taken, then the next `limit` is also `true`. If `limit` is `true` but the maximum value has not been taken, or if `limit` is `false`, then the next `limit` is `false`. +- The integer $i$ represents the current position in the string $s$. +- The boolean $\textit{lead}$ indicates whether the current number contains only leading zeros. +- The boolean $\textit{limit}$ indicates whether the current position is restricted by the upper bound. -For the implementation details of the function, you can refer to the code below. +The function executes as follows: -The time complexity is $O(\log n)$. +If $i$ is greater than or equal to $m$, it means we have processed all digits. If $\textit{lead}$ is true, it means the current number is a leading zero, and we should return $0$. Otherwise, we should return $1$. + +Otherwise, we calculate the upper bound $\textit{up}$. If $\textit{limit}$ is true, then $\textit{up}$ is the digit corresponding to $s[i]$. Otherwise, $\textit{up}$ is $9$. + +Then, we enumerate the current digit $j$ in the range $[0, \textit{up}]$. If $j$ is $0$ and $\textit{lead}$ is true, we recursively calculate $\textit{dfs}(i + 1, \text{true}, \textit{limit} \wedge j = \textit{up})$. Otherwise, if $j$ is in $\textit{digits}$, we recursively calculate $\textit{dfs}(i + 1, \text{false}, \textit{limit} \wedge j = \textit{up})$. We accumulate all the results as the answer. + +Finally, we return $\textit{dfs}(0, \text{true}, \text{true})$. + +The time complexity is $O(\log n \times D)$, and the space complexity is $O(\log n)$. Here, $D = 10$. Similar problems: -- [233. Number of Digit One](https://github.com/doocs/leetcode/blob/main/solution/0200-0299/0233.Number%20of%20Digit%20One/README.md) -- [357. Count Numbers with Unique Digits](https://github.com/doocs/leetcode/blob/main/solution/0300-0399/0357.Count%20Numbers%20with%20Unique%20Digits/README.md) -- [600. Non-negative Integers without Consecutive Ones](https://github.com/doocs/leetcode/blob/main/solution/0600-0699/0600.Non-negative%20Integers%20without%20Consecutive%20Ones/README.md) -- [788. Rotated Digits](https://github.com/doocs/leetcode/blob/main/solution/0700-0799/0788.Rotated%20Digits/README.md) -- [1012. Numbers With Repeated Digits](https://github.com/doocs/leetcode/blob/main/solution/1000-1099/1012.Numbers%20With%20Repeated%20Digits/README.md) -- [2376. Count Special Integers](https://github.com/doocs/leetcode/blob/main/solution/2300-2399/2376.Count%20Special%20Integers/README.md) +- [233. Number of Digit One](https://github.com/doocs/leetcode/blob/main/solution/0200-0299/0233.Number%20of%20Digit%20One/README_EN.md) +- [357. Count Numbers with Unique Digits](https://github.com/doocs/leetcode/blob/main/solution/0300-0399/0357.Count%20Numbers%20with%20Unique%20Digits/README_EN.md) +- [600. Non-negative Integers without Consecutive Ones](https://github.com/doocs/leetcode/blob/main/solution/0600-0699/0600.Non-negative%20Integers%20without%20Consecutive%20Ones/README_EN.md) +- [788. Rotated Digits](https://github.com/doocs/leetcode/blob/main/solution/0700-0799/0788.Rotated%20Digits/README_EN.md) +- [1012. Numbers With Repeated Digits](https://github.com/doocs/leetcode/blob/main/solution/1000-1099/1012.Numbers%20With%20Repeated%20Digits/README_EN.md) +- [2376. Count Special Integers](https://github.com/doocs/leetcode/blob/main/solution/2300-2399/2376.Count%20Special%20Integers/README_EN.md) @@ -118,69 +125,59 @@ Similar problems: class Solution: def atMostNGivenDigitSet(self, digits: List[str], n: int) -> int: @cache - def dfs(pos, lead, limit): - if pos <= 0: - return lead == False - up = a[pos] if limit else 9 + def dfs(i: int, lead: int, limit: bool) -> int: + if i >= len(s): + return lead ^ 1 + + up = int(s[i]) if limit else 9 ans = 0 - for i in range(up + 1): - if i == 0 and lead: - ans += dfs(pos - 1, lead, limit and i == up) - elif i in s: - ans += dfs(pos - 1, False, limit and i == up) + for j in range(up + 1): + if j == 0 and lead: + ans += dfs(i + 1, 1, limit and j == up) + elif j in nums: + ans += dfs(i + 1, 0, limit and j == up) return ans - l = 0 - a = [0] * 12 - s = {int(d) for d in digits} - while n: - l += 1 - a[l] = n % 10 - n //= 10 - return dfs(l, True, True) + s = str(n) + nums = {int(x) for x in digits} + return dfs(0, 1, True) ``` #### Java ```java class Solution { - private int[] a = new int[12]; - private int[][] dp = new int[12][2]; - private Set s = new HashSet<>(); + private Set nums = new HashSet<>(); + private char[] s; + private Integer[] f; public int atMostNGivenDigitSet(String[] digits, int n) { - for (var e : dp) { - Arrays.fill(e, -1); + s = String.valueOf(n).toCharArray(); + f = new Integer[s.length]; + for (var x : digits) { + nums.add(Integer.parseInt(x)); } - for (String d : digits) { - s.add(Integer.parseInt(d)); - } - int len = 0; - while (n > 0) { - a[++len] = n % 10; - n /= 10; - } - return dfs(len, 1, true); + return dfs(0, true, true); } - private int dfs(int pos, int lead, boolean limit) { - if (pos <= 0) { - return lead ^ 1; + private int dfs(int i, boolean lead, boolean limit) { + if (i >= s.length) { + return lead ? 0 : 1; } - if (!limit && lead != 1 && dp[pos][lead] != -1) { - return dp[pos][lead]; + if (!lead && !limit && f[i] != null) { + return f[i]; } + int up = limit ? s[i] - '0' : 9; int ans = 0; - int up = limit ? a[pos] : 9; - for (int i = 0; i <= up; ++i) { - if (i == 0 && lead == 1) { - ans += dfs(pos - 1, lead, limit && i == up); - } else if (s.contains(i)) { - ans += dfs(pos - 1, 0, limit && i == up); + for (int j = 0; j <= up; ++j) { + if (j == 0 && lead) { + ans += dfs(i + 1, true, limit && j == up); + } else if (nums.contains(j)) { + ans += dfs(i + 1, false, limit && j == up); } } - if (!limit && lead == 0) { - dp[pos][lead] = ans; + if (!lead && !limit) { + f[i] = ans; } return ans; } @@ -192,43 +189,37 @@ class Solution { ```cpp class Solution { public: - int a[12]; - int dp[12][2]; - unordered_set s; - int atMostNGivenDigitSet(vector& digits, int n) { - memset(dp, -1, sizeof dp); - for (auto& d : digits) { - s.insert(stoi(d)); - } - int len = 0; - while (n) { - a[++len] = n % 10; - n /= 10; + string s = to_string(n); + unordered_set nums; + for (auto& x : digits) { + nums.insert(stoi(x)); } - return dfs(len, 1, true); - } - - int dfs(int pos, int lead, bool limit) { - if (pos <= 0) { - return lead ^ 1; - } - if (!limit && !lead && dp[pos][lead] != -1) { - return dp[pos][lead]; - } - int ans = 0; - int up = limit ? a[pos] : 9; - for (int i = 0; i <= up; ++i) { - if (i == 0 && lead) { - ans += dfs(pos - 1, lead, limit && i == up); - } else if (s.count(i)) { - ans += dfs(pos - 1, 0, limit && i == up); + int m = s.size(); + int f[m]; + memset(f, -1, sizeof(f)); + auto dfs = [&](auto&& dfs, int i, bool lead, bool limit) -> int { + if (i >= m) { + return lead ? 0 : 1; } - } - if (!limit && !lead) { - dp[pos][lead] = ans; - } - return ans; + if (!lead && !limit && f[i] != -1) { + return f[i]; + } + int up = limit ? s[i] - '0' : 9; + int ans = 0; + for (int j = 0; j <= up; ++j) { + if (j == 0 && lead) { + ans += dfs(dfs, i + 1, true, limit && j == up); + } else if (nums.count(j)) { + ans += dfs(dfs, i + 1, false, limit && j == up); + } + } + if (!lead && !limit) { + f[i] = ans; + } + return ans; + }; + return dfs(dfs, 0, true, true); } }; ``` @@ -237,48 +228,79 @@ public: ```go func atMostNGivenDigitSet(digits []string, n int) int { - s := map[int]bool{} - for _, d := range digits { - i, _ := strconv.Atoi(d) - s[i] = true + s := strconv.Itoa(n) + m := len(s) + f := make([]int, m) + for i := range f { + f[i] = -1 } - a := make([]int, 12) - dp := make([][2]int, 12) - for i := range a { - dp[i] = [2]int{-1, -1} - } - l := 0 - for n > 0 { - l++ - a[l] = n % 10 - n /= 10 + nums := map[int]bool{} + for _, d := range digits { + x, _ := strconv.Atoi(d) + nums[x] = true } - var dfs func(int, int, bool) int - dfs = func(pos, lead int, limit bool) int { - if pos <= 0 { - return lead ^ 1 + var dfs func(i int, lead, limit bool) int + dfs = func(i int, lead, limit bool) int { + if i >= m { + if lead { + return 0 + } + return 1 } - if !limit && lead == 0 && dp[pos][lead] != -1 { - return dp[pos][lead] + if !lead && !limit && f[i] != -1 { + return f[i] } up := 9 if limit { - up = a[pos] + up = int(s[i] - '0') } ans := 0 - for i := 0; i <= up; i++ { - if i == 0 && lead == 1 { - ans += dfs(pos-1, lead, limit && i == up) - } else if s[i] { - ans += dfs(pos-1, 0, limit && i == up) + for j := 0; j <= up; j++ { + if j == 0 && lead { + ans += dfs(i+1, true, limit && j == up) + } else if nums[j] { + ans += dfs(i+1, false, limit && j == up) } } - if !limit { - dp[pos][lead] = ans + if !lead && !limit { + f[i] = ans } return ans } - return dfs(l, 1, true) + return dfs(0, true, true) +} +``` + +#### TypeScript + +```ts +function atMostNGivenDigitSet(digits: string[], n: number): number { + const s = n.toString(); + const m = s.length; + const f: number[] = Array(m).fill(-1); + const nums = new Set(digits.map(d => parseInt(d))); + const dfs = (i: number, lead: boolean, limit: boolean): number => { + if (i >= m) { + return lead ? 0 : 1; + } + if (!lead && !limit && f[i] !== -1) { + return f[i]; + } + const up = limit ? +s[i] : 9; + let ans = 0; + for (let j = 0; j <= up; ++j) { + if (!j && lead) { + ans += dfs(i + 1, true, limit && j === up); + } else if (nums.has(j)) { + ans += dfs(i + 1, false, limit && j === up); + } + } + if (!lead && !limit) { + f[i] = ans; + } + return ans; + }; + return dfs(0, true, true); } ``` diff --git a/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.cpp b/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.cpp index 15bfd4ba18501..fe7fc4cda0907 100644 --- a/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.cpp +++ b/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.cpp @@ -1,41 +1,35 @@ class Solution { public: - int a[12]; - int dp[12][2]; - unordered_set s; - int atMostNGivenDigitSet(vector& digits, int n) { - memset(dp, -1, sizeof dp); - for (auto& d : digits) { - s.insert(stoi(d)); + string s = to_string(n); + unordered_set nums; + for (auto& x : digits) { + nums.insert(stoi(x)); } - int len = 0; - while (n) { - a[++len] = n % 10; - n /= 10; - } - return dfs(len, 1, true); - } - - int dfs(int pos, int lead, bool limit) { - if (pos <= 0) { - return lead ^ 1; - } - if (!limit && !lead && dp[pos][lead] != -1) { - return dp[pos][lead]; - } - int ans = 0; - int up = limit ? a[pos] : 9; - for (int i = 0; i <= up; ++i) { - if (i == 0 && lead) { - ans += dfs(pos - 1, lead, limit && i == up); - } else if (s.count(i)) { - ans += dfs(pos - 1, 0, limit && i == up); + int m = s.size(); + int f[m]; + memset(f, -1, sizeof(f)); + auto dfs = [&](auto&& dfs, int i, bool lead, bool limit) -> int { + if (i >= m) { + return lead ? 0 : 1; } - } - if (!limit && !lead) { - dp[pos][lead] = ans; - } - return ans; + if (!lead && !limit && f[i] != -1) { + return f[i]; + } + int up = limit ? s[i] - '0' : 9; + int ans = 0; + for (int j = 0; j <= up; ++j) { + if (j == 0 && lead) { + ans += dfs(dfs, i + 1, true, limit && j == up); + } else if (nums.count(j)) { + ans += dfs(dfs, i + 1, false, limit && j == up); + } + } + if (!lead && !limit) { + f[i] = ans; + } + return ans; + }; + return dfs(dfs, 0, true, true); } }; \ No newline at end of file diff --git a/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.go b/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.go index 77fa593871908..5650fd2fe56f4 100644 --- a/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.go +++ b/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.go @@ -1,44 +1,42 @@ func atMostNGivenDigitSet(digits []string, n int) int { - s := map[int]bool{} - for _, d := range digits { - i, _ := strconv.Atoi(d) - s[i] = true - } - a := make([]int, 12) - dp := make([][2]int, 12) - for i := range a { - dp[i] = [2]int{-1, -1} + s := strconv.Itoa(n) + m := len(s) + f := make([]int, m) + for i := range f { + f[i] = -1 } - l := 0 - for n > 0 { - l++ - a[l] = n % 10 - n /= 10 + nums := map[int]bool{} + for _, d := range digits { + x, _ := strconv.Atoi(d) + nums[x] = true } - var dfs func(int, int, bool) int - dfs = func(pos, lead int, limit bool) int { - if pos <= 0 { - return lead ^ 1 + var dfs func(i int, lead, limit bool) int + dfs = func(i int, lead, limit bool) int { + if i >= m { + if lead { + return 0 + } + return 1 } - if !limit && lead == 0 && dp[pos][lead] != -1 { - return dp[pos][lead] + if !lead && !limit && f[i] != -1 { + return f[i] } up := 9 if limit { - up = a[pos] + up = int(s[i] - '0') } ans := 0 - for i := 0; i <= up; i++ { - if i == 0 && lead == 1 { - ans += dfs(pos-1, lead, limit && i == up) - } else if s[i] { - ans += dfs(pos-1, 0, limit && i == up) + for j := 0; j <= up; j++ { + if j == 0 && lead { + ans += dfs(i+1, true, limit && j == up) + } else if nums[j] { + ans += dfs(i+1, false, limit && j == up) } } - if !limit { - dp[pos][lead] = ans + if !lead && !limit { + f[i] = ans } return ans } - return dfs(l, 1, true) + return dfs(0, true, true) } \ No newline at end of file diff --git a/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.java b/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.java index dcfa7329f711c..dbee36728ba00 100644 --- a/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.java +++ b/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.java @@ -1,41 +1,35 @@ class Solution { - private int[] a = new int[12]; - private int[][] dp = new int[12][2]; - private Set s = new HashSet<>(); + private Set nums = new HashSet<>(); + private char[] s; + private Integer[] f; public int atMostNGivenDigitSet(String[] digits, int n) { - for (var e : dp) { - Arrays.fill(e, -1); + s = String.valueOf(n).toCharArray(); + f = new Integer[s.length]; + for (var x : digits) { + nums.add(Integer.parseInt(x)); } - for (String d : digits) { - s.add(Integer.parseInt(d)); - } - int len = 0; - while (n > 0) { - a[++len] = n % 10; - n /= 10; - } - return dfs(len, 1, true); + return dfs(0, true, true); } - private int dfs(int pos, int lead, boolean limit) { - if (pos <= 0) { - return lead ^ 1; + private int dfs(int i, boolean lead, boolean limit) { + if (i >= s.length) { + return lead ? 0 : 1; } - if (!limit && lead != 1 && dp[pos][lead] != -1) { - return dp[pos][lead]; + if (!lead && !limit && f[i] != null) { + return f[i]; } + int up = limit ? s[i] - '0' : 9; int ans = 0; - int up = limit ? a[pos] : 9; - for (int i = 0; i <= up; ++i) { - if (i == 0 && lead == 1) { - ans += dfs(pos - 1, lead, limit && i == up); - } else if (s.contains(i)) { - ans += dfs(pos - 1, 0, limit && i == up); + for (int j = 0; j <= up; ++j) { + if (j == 0 && lead) { + ans += dfs(i + 1, true, limit && j == up); + } else if (nums.contains(j)) { + ans += dfs(i + 1, false, limit && j == up); } } - if (!limit && lead == 0) { - dp[pos][lead] = ans; + if (!lead && !limit) { + f[i] = ans; } return ans; } diff --git a/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.py b/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.py index 2df70aff4cb56..fc0cd5dd9e6d6 100644 --- a/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.py +++ b/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.py @@ -1,23 +1,19 @@ class Solution: def atMostNGivenDigitSet(self, digits: List[str], n: int) -> int: @cache - def dfs(pos, lead, limit): - if pos <= 0: - return lead == False - up = a[pos] if limit else 9 + def dfs(i: int, lead: int, limit: bool) -> int: + if i >= len(s): + return lead ^ 1 + + up = int(s[i]) if limit else 9 ans = 0 - for i in range(up + 1): - if i == 0 and lead: - ans += dfs(pos - 1, lead, limit and i == up) - elif i in s: - ans += dfs(pos - 1, False, limit and i == up) + for j in range(up + 1): + if j == 0 and lead: + ans += dfs(i + 1, 1, limit and j == up) + elif j in nums: + ans += dfs(i + 1, 0, limit and j == up) return ans - l = 0 - a = [0] * 12 - s = {int(d) for d in digits} - while n: - l += 1 - a[l] = n % 10 - n //= 10 - return dfs(l, True, True) + s = str(n) + nums = {int(x) for x in digits} + return dfs(0, 1, True) diff --git a/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.ts b/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.ts new file mode 100644 index 0000000000000..b3dc835cd1937 --- /dev/null +++ b/solution/0900-0999/0902.Numbers At Most N Given Digit Set/Solution.ts @@ -0,0 +1,28 @@ +function atMostNGivenDigitSet(digits: string[], n: number): number { + const s = n.toString(); + const m = s.length; + const f: number[] = Array(m).fill(-1); + const nums = new Set(digits.map(d => parseInt(d))); + const dfs = (i: number, lead: boolean, limit: boolean): number => { + if (i >= m) { + return lead ? 0 : 1; + } + if (!lead && !limit && f[i] !== -1) { + return f[i]; + } + const up = limit ? +s[i] : 9; + let ans = 0; + for (let j = 0; j <= up; ++j) { + if (!j && lead) { + ans += dfs(i + 1, true, limit && j === up); + } else if (nums.has(j)) { + ans += dfs(i + 1, false, limit && j === up); + } + } + if (!lead && !limit) { + f[i] = ans; + } + return ans; + }; + return dfs(0, true, true); +}