From 112a4375e02c9cb92c1e218e5ec10fdc4523169b Mon Sep 17 00:00:00 2001 From: yanglbme Date: Fri, 20 Sep 2024 22:31:44 +0800 Subject: [PATCH] feat: update solutions to lc problem: No.1012 No.1012.Numbers With Repeated Digits --- .../0700-0799/0788.Rotated Digits/README.md | 2 +- .../README.md | 461 +++++------------- .../README_EN.md | 448 +++++------------ .../Solution.cpp | 55 +-- .../Solution.go | 69 ++- .../Solution.java | 50 +- .../Solution.py | 38 +- .../Solution.ts | 40 +- .../Solution2.cpp | 44 -- .../Solution2.go | 48 -- .../Solution2.java | 41 -- .../Solution2.py | 25 - 12 files changed, 357 insertions(+), 964 deletions(-) delete mode 100644 solution/1000-1099/1012.Numbers With Repeated Digits/Solution2.cpp delete mode 100644 solution/1000-1099/1012.Numbers With Repeated Digits/Solution2.go delete mode 100644 solution/1000-1099/1012.Numbers With Repeated Digits/Solution2.java delete mode 100644 solution/1000-1099/1012.Numbers With Repeated Digits/Solution2.py diff --git a/solution/0700-0799/0788.Rotated Digits/README.md b/solution/0700-0799/0788.Rotated Digits/README.md index 5299736cd7e98..dba29eb20f432 100644 --- a/solution/0700-0799/0788.Rotated Digits/README.md +++ b/solution/0700-0799/0788.Rotated Digits/README.md @@ -29,7 +29,7 @@ tags:
输入: 10
 输出: 4
-解释:
+解释: 
 在[1, 10]中有四个好数: 2, 5, 6, 9。
 注意 1 和 10 不是好数, 因为他们在旋转之后不变。
 
diff --git a/solution/1000-1099/1012.Numbers With Repeated Digits/README.md b/solution/1000-1099/1012.Numbers With Repeated Digits/README.md index c735405c06e05..5db025d60d7f7 100644 --- a/solution/1000-1099/1012.Numbers With Repeated Digits/README.md +++ b/solution/1000-1099/1012.Numbers With Repeated Digits/README.md @@ -70,19 +70,24 @@ tags: 基本步骤如下: -1. 将数字 $n$ 转为整型数组 $nums$,其中 $nums[0]$ 为最低位,而 $nums[i]$ 为最高位; -1. 根据题目信息,设计函数 $dfs()$,对于本题,我们定义 $dfs(pos, mask, lead, limit)$,其中: +我们将数字 $n$ 转为字符串 $s$,接下来,我们设计一个函数 $\textit{dfs}(i, \textit{mask}, \textit{lead}, \textit{limit})$,其中: -- 参数 $pos$ 表示当前搜索到的数字的位数,从末位或者第一位开始,一般根据题目的数字构造性质来选择顺序。对于本题,我们选择从高位开始,因此 $pos$ 的初始值为数字的高位下标; -- 参数 $mask$ 表示当前数字中出现过的数字; -- 参数 $lead$ 表示当前数字是否仅包含前导零; -- 参数 $limit$ 表示当前可填的数字的限制,如果无限制,那么可以选择 $i \in [0,1,..9]$,否则,只能选择 $i \in [0,..nums[pos]]$。如果 $limit$ 为 `true` 且已经取到了能取到的最大值,那么下一个 $limit$ 同样为 `true`;如果 $limit$ 为 `true` 但是还没有取到最大值,或者 $limit$ 为 `false`,那么下一个 $limit$ 为 `false`。 +- 数字 $i$ 表示当前处理的数字下标,从 $0$ 开始。 +- 数字 $\textit{mask}$ 表示当前数字中出现过的数字,用二进制数表示。其中 $\textit{mask}$ 的二进制的第 $j$ 位为 $1$ 表示数字 $j$ 出现过,为 $0$ 表示数字 $j$ 没有出现过。 +- 布尔值 $\textit{lead}$ 表示是否只包含前导零。 +- 布尔值 $\textit{limit}$ 表示当前位置是否受到上界的限制。 -答案为 $dfs(0, 0, true, true)$。 +函数的执行过程如下: -关于函数的实现细节,可以参考下面的代码。 +如果 $i$ 大于等于 $m$,说明我们已经处理完了所有的位数,此时如果 $\textit{lead}$ 为真,说明当前的数字是前导零,我们应当返回 $0$;否则,我们应当返回 $1$。 -时间复杂度 $O(m \times 2^m \times 10)$,空间复杂度 $O(m \times 2^m)$。其中 $m$ 为数字 $n$ 的位数。 +否则,我们计算当前位置的上界 $\textit{up}$,如果 $\textit{limit}$ 为真,则 $up$ 为 $s[i]$ 对应的数字,否则 $up$ 为 $9$。 + +然后,我们在 $[0, \textit{up}]$ 的范围内枚举当前位置的数字 $j$,如果 $j$ 为 $0$ 且 $\textit{lead}$ 为真,我们递归计算 $\textit{dfs}(i + 1, \textit{mask}, \text{true}, \textit{limit} \wedge j = \textit{up})$;否则,如果 $\textit{mask}$ 的第 $j$ 位为 $0$,我们递归计算 $\textit{dfs}(i + 1, \textit{mask} \,|\, 2^j, \text{false}, \textit{limit} \wedge j = \textit{up})$。累加所有的结果即为答案。 + +答案为 $n - \textit{dfs}(0, 0, \text{true}, \text{true})$。 + +时间复杂度 $O(\log n \times 2^D \times D)$,空间复杂度 $O(\log n \times 2^D)$。其中 $D = 10$。 相似题目: @@ -100,294 +105,54 @@ tags: ```python class Solution: def numDupDigitsAtMostN(self, n: int) -> int: - return n - self.f(n) - - def f(self, n): - def A(m, n): - return 1 if n == 0 else A(m, n - 1) * (m - n + 1) - - vis = [False] * 10 - ans = 0 - digits = [int(c) for c in str(n)[::-1]] - m = len(digits) - for i in range(1, m): - ans += 9 * A(9, i - 1) - for i in range(m - 1, -1, -1): - v = digits[i] - j = 1 if i == m - 1 else 0 - while j < v: - if not vis[j]: - ans += A(10 - (m - i), i) - j += 1 - if vis[v]: - break - vis[v] = True - if i == 0: - ans += 1 - return ans -``` - -#### Java - -```java -class Solution { - public int numDupDigitsAtMostN(int n) { - return n - f(n); - } - - public int f(int n) { - List digits = new ArrayList<>(); - while (n != 0) { - digits.add(n % 10); - n /= 10; - } - int m = digits.size(); - int ans = 0; - for (int i = 1; i < m; ++i) { - ans += 9 * A(9, i - 1); - } - boolean[] vis = new boolean[10]; - for (int i = m - 1; i >= 0; --i) { - int v = digits.get(i); - for (int j = i == m - 1 ? 1 : 0; j < v; ++j) { - if (vis[j]) { - continue; - } - ans += A(10 - (m - i), i); - } - if (vis[v]) { - break; - } - vis[v] = true; - if (i == 0) { - ++ans; - } - } - return ans; - } - - private int A(int m, int n) { - return n == 0 ? 1 : A(m, n - 1) * (m - n + 1); - } -} -``` - -#### C++ - -```cpp -class Solution { -public: - int numDupDigitsAtMostN(int n) { - return n - f(n); - } - - int f(int n) { - int ans = 0; - vector digits; - while (n) { - digits.push_back(n % 10); - n /= 10; - } - int m = digits.size(); - vector vis(10); - for (int i = 1; i < m; ++i) { - ans += 9 * A(9, i - 1); - } - for (int i = m - 1; ~i; --i) { - int v = digits[i]; - for (int j = i == m - 1 ? 1 : 0; j < v; ++j) { - if (!vis[j]) { - ans += A(10 - (m - i), i); - } - } - if (vis[v]) { - break; - } - vis[v] = true; - if (i == 0) { - ++ans; - } - } - return ans; - } - - int A(int m, int n) { - return n == 0 ? 1 : A(m, n - 1) * (m - n + 1); - } -}; -``` - -#### Go - -```go -func numDupDigitsAtMostN(n int) int { - return n - f(n) -} - -func f(n int) int { - digits := []int{} - for n != 0 { - digits = append(digits, n%10) - n /= 10 - } - m := len(digits) - vis := make([]bool, 10) - ans := 0 - for i := 1; i < m; i++ { - ans += 9 * A(9, i-1) - } - for i := m - 1; i >= 0; i-- { - v := digits[i] - j := 0 - if i == m-1 { - j = 1 - } - for ; j < v; j++ { - if !vis[j] { - ans += A(10-(m-i), i) - } - } - if vis[v] { - break - } - vis[v] = true - if i == 0 { - ans++ - } - } - return ans -} - -func A(m, n int) int { - if n == 0 { - return 1 - } - return A(m, n-1) * (m - n + 1) -} -``` - -#### TypeScript - -```ts -function numDupDigitsAtMostN(n: number): number { - return n - f(n); -} - -function f(n: number): number { - const nums: number[] = []; - let i = -1; - for (; n; n = Math.floor(n / 10)) { - nums[++i] = n % 10; - } - const dp = Array.from({ length: 11 }, () => Array(1 << 11).fill(-1)); - const dfs = (pos: number, mask: number, lead: boolean, limit: boolean): number => { - if (pos < 0) { - return lead ? 0 : 1; - } - if (!lead && !limit && dp[pos][mask] !== -1) { - return dp[pos][mask]; - } - const up = limit ? nums[pos] : 9; - let ans = 0; - for (let i = 0; i <= up; ++i) { - if ((mask >> i) & 1) { - continue; - } - if (lead && i === 0) { - ans += dfs(pos - 1, mask, lead, limit && i === up); - } else { - ans += dfs(pos - 1, mask | (1 << i), false, limit && i === up); - } - } - if (!lead && !limit) { - dp[pos][mask] = ans; - } - return ans; - }; - return dfs(i, 0, true, true); -} -``` - - - - - - - -### 方法二 - - - -#### Python3 - -```python -class Solution: - def numDupDigitsAtMostN(self, n: int) -> int: - return n - self.f(n) - - def f(self, n: int) -> int: @cache - def dfs(pos: int, mask: int, lead: bool, limit: bool) -> int: - if pos < 0: - return int(lead) ^ 1 - up = nums[pos] if limit else 9 + def dfs(i: int, mask: int, lead: bool, 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 mask >> i & 1: - continue - if i == 0 and lead: - ans += dfs(pos - 1, mask, lead, limit and i == up) - else: - ans += dfs(pos - 1, mask | 1 << i, False, limit and i == up) + for j in range(up + 1): + if lead and j == 0: + ans += dfs(i + 1, mask, True, False) + elif mask >> j & 1 ^ 1: + ans += dfs(i + 1, mask | 1 << j, False, limit and j == up) return ans - nums = [] - while n: - nums.append(n % 10) - n //= 10 - return dfs(len(nums) - 1, 0, True, True) + s = str(n) + return n - dfs(0, 0, True, True) ``` #### Java ```java class Solution { - private int[] nums = new int[11]; - private Integer[][] dp = new Integer[11][1 << 11]; + private char[] s; + private Integer[][] f; public int numDupDigitsAtMostN(int n) { - return n - f(n); + s = String.valueOf(n).toCharArray(); + f = new Integer[s.length][1 << 10]; + return n - dfs(0, 0, true, true); } - private int f(int n) { - int i = -1; - for (; n > 0; n /= 10) { - nums[++i] = n % 10; - } - return dfs(i, 0, true, true); - } - - private int dfs(int pos, int mask, boolean lead, boolean limit) { - if (pos < 0) { + private int dfs(int i, int mask, boolean lead, boolean limit) { + if (i >= s.length) { return lead ? 0 : 1; } - if (!lead && !limit && dp[pos][mask] != null) { - return dp[pos][mask]; + if (!lead && !limit && f[i][mask] != null) { + return f[i][mask]; } + int up = limit ? s[i] - '0' : 9; int ans = 0; - int up = limit ? nums[pos] : 9; - for (int i = 0; i <= up; ++i) { - if ((mask >> i & 1) == 1) { - continue; - } - if (i == 0 && lead) { - ans += dfs(pos - 1, mask, lead, limit && i == up); - } else { - ans += dfs(pos - 1, mask | 1 << i, false, limit && i == up); + for (int j = 0; j <= up; ++j) { + if (lead && j == 0) { + ans += dfs(i + 1, mask, true, false); + } else if ((mask >> j & 1) == 0) { + ans += dfs(i + 1, mask | 1 << j, false, limit && j == up); } } if (!lead && !limit) { - dp[pos][mask] = ans; + f[i][mask] = ans; } return ans; } @@ -400,45 +165,32 @@ class Solution { class Solution { public: int numDupDigitsAtMostN(int n) { - return n - f(n); - } - -private: - int nums[11]; - int dp[11][1 << 11]; - - int f(int n) { - memset(dp, -1, sizeof(dp)); - int i = -1; - for (; n; n /= 10) { - nums[++i] = n % 10; - } - return dfs(i, 0, true, true); - } - - int dfs(int pos, int mask, bool lead, bool limit) { - if (pos < 0) { - return lead ? 0 : 1; - } - if (!lead && !limit && dp[pos][mask] != -1) { - return dp[pos][mask]; - } - int up = limit ? nums[pos] : 9; - int ans = 0; - for (int i = 0; i <= up; ++i) { - if (mask >> i & 1) { - continue; + string s = to_string(n); + int m = s.size(); + int f[m][1 << 10]; + memset(f, -1, sizeof(f)); + auto dfs = [&](auto&& dfs, int i, int mask, bool lead, bool limit) -> int { + if (i >= m) { + return lead ^ 1; } - if (i == 0 && lead) { - ans += dfs(pos - 1, mask, lead, limit && i == up); - } else { - ans += dfs(pos - 1, mask | 1 << i, false, limit && i == up); + if (!lead && !limit && f[i][mask] != -1) { + return f[i][mask]; } - } - if (!lead && !limit) { - dp[pos][mask] = ans; - } - return ans; + int up = limit ? s[i] - '0' : 9; + int ans = 0; + for (int j = 0; j <= up; ++j) { + if (lead && j == 0) { + ans += dfs(dfs, i + 1, mask, true, limit && j == up); + } else if (mask >> j & 1 ^ 1) { + ans += dfs(dfs, i + 1, mask | (1 << j), false, limit && j == up); + } + } + if (!lead && !limit) { + f[i][mask] = ans; + } + return ans; + }; + return n - dfs(dfs, 0, 0, true, true); } }; ``` @@ -447,52 +199,79 @@ private: ```go func numDupDigitsAtMostN(n int) int { - return n - f(n) -} - -func f(n int) int { - nums := []int{} - for ; n > 0; n /= 10 { - nums = append(nums, n%10) - } - dp := [11][1 << 11]int{} - for i := range dp { - for j := range dp[i] { - dp[i][j] = -1 + s := []byte(strconv.Itoa(n)) + m := len(s) + f := make([][]int, m) + for i := range f { + f[i] = make([]int, 1<<10) + for j := range f[i] { + f[i][j] = -1 } } - var dfs func(int, int, bool, bool) int - dfs = func(pos int, mask int, lead bool, limit bool) int { - if pos < 0 { + + var dfs func(i, mask int, lead, limit bool) int + dfs = func(i, mask int, lead, limit bool) int { + if i >= m { if lead { return 0 } return 1 } - if !lead && !limit && dp[pos][mask] != -1 { - return dp[pos][mask] + if !lead && !limit && f[i][mask] != -1 { + return f[i][mask] } up := 9 if limit { - up = nums[pos] + up = int(s[i] - '0') } ans := 0 - for i := 0; i <= up; i++ { - if mask>>i&1 == 1 { - continue - } - if i == 0 && lead { - ans += dfs(pos-1, mask, lead, limit && i == up) - } else { - ans += dfs(pos-1, mask|1<>j&1 == 0 { + ans += dfs(i+1, mask|(1< Array(1 << 10).fill(-1)); + + const dfs = (i: number, mask: number, lead: boolean, limit: boolean): number => { + if (i >= m) { + return lead ? 0 : 1; + } + if (!lead && !limit && f[i][mask] !== -1) { + return f[i][mask]; + } + const up = limit ? parseInt(s[i]) : 9; + let ans = 0; + for (let j = 0; j <= up; j++) { + if (lead && j === 0) { + ans += dfs(i + 1, mask, true, limit && j === up); + } else if (((mask >> j) & 1) === 0) { + ans += dfs(i + 1, mask | (1 << j), false, limit && j === up); + } + } + if (!lead && !limit) { + f[i][mask] = ans; + } + return ans; + }; + + return n - dfs(0, 0, true, true); } ``` diff --git a/solution/1000-1099/1012.Numbers With Repeated Digits/README_EN.md b/solution/1000-1099/1012.Numbers With Repeated Digits/README_EN.md index 75d0d1f85a092..f4d48ffe014b0 100644 --- a/solution/1000-1099/1012.Numbers With Repeated Digits/README_EN.md +++ b/solution/1000-1099/1012.Numbers With Repeated Digits/README_EN.md @@ -58,230 +58,43 @@ tags: -### Solution 1 +### Solution 1: State Compression + Digit DP - - -#### Python3 +The problem requires counting the number of integers in the range $[1, .., n]$ that have at least one repeated digit. We can approach this by defining a function $f(n)$ that counts the number of integers in the range $[1, .., n]$ with no repeated digits. Then, the answer is $n - f(n)$. -```python -class Solution: - def numDupDigitsAtMostN(self, n: int) -> int: - return n - self.f(n) - - def f(self, n): - def A(m, n): - return 1 if n == 0 else A(m, n - 1) * (m - n + 1) - - vis = [False] * 10 - ans = 0 - digits = [int(c) for c in str(n)[::-1]] - m = len(digits) - for i in range(1, m): - ans += 9 * A(9, i - 1) - for i in range(m - 1, -1, -1): - v = digits[i] - j = 1 if i == m - 1 else 0 - while j < v: - if not vis[j]: - ans += A(10 - (m - i), i) - j += 1 - if vis[v]: - break - vis[v] = True - if i == 0: - ans += 1 - return ans -``` - -#### Java +Additionally, we can use a binary number to record the digits that have appeared in the number. For example, if the digits $1$, $2$, and $4$ have appeared, the corresponding binary number is $\underline{1}0\underline{1}\underline{1}0$. -```java -class Solution { - public int numDupDigitsAtMostN(int n) { - return n - f(n); - } - - public int f(int n) { - List digits = new ArrayList<>(); - while (n != 0) { - digits.add(n % 10); - n /= 10; - } - int m = digits.size(); - int ans = 0; - for (int i = 1; i < m; ++i) { - ans += 9 * A(9, i - 1); - } - boolean[] vis = new boolean[10]; - for (int i = m - 1; i >= 0; --i) { - int v = digits.get(i); - for (int j = i == m - 1 ? 1 : 0; j < v; ++j) { - if (vis[j]) { - continue; - } - ans += A(10 - (m - i), i); - } - if (vis[v]) { - break; - } - vis[v] = true; - if (i == 0) { - ++ans; - } - } - return ans; - } +Next, 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. - private int A(int m, int n) { - return n == 0 ? 1 : A(m, n - 1) * (m - n + 1); - } -} -``` +The basic steps are as follows: -#### C++ +We convert the number $n$ into a string $s$. Next, we design a function $\textit{dfs}(i, \textit{mask}, \textit{lead}, \textit{limit})$, where: -```cpp -class Solution { -public: - int numDupDigitsAtMostN(int n) { - return n - f(n); - } +- The integer $i$ represents the current digit index, starting from $0$. +- The integer $\textit{mask}$ represents the digits that have appeared so far, using a binary number. The $j$-th bit of $\textit{mask}$ being $1$ indicates that digit $j$ has appeared, while $0$ indicates it has not. +- 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. - int f(int n) { - int ans = 0; - vector digits; - while (n) { - digits.push_back(n % 10); - n /= 10; - } - int m = digits.size(); - vector vis(10); - for (int i = 1; i < m; ++i) { - ans += 9 * A(9, i - 1); - } - for (int i = m - 1; ~i; --i) { - int v = digits[i]; - for (int j = i == m - 1 ? 1 : 0; j < v; ++j) { - if (!vis[j]) { - ans += A(10 - (m - i), i); - } - } - if (vis[v]) { - break; - } - vis[v] = true; - if (i == 0) { - ++ans; - } - } - return ans; - } - - int A(int m, int n) { - return n == 0 ? 1 : A(m, n - 1) * (m - n + 1); - } -}; -``` - -#### Go - -```go -func numDupDigitsAtMostN(n int) int { - return n - f(n) -} - -func f(n int) int { - digits := []int{} - for n != 0 { - digits = append(digits, n%10) - n /= 10 - } - m := len(digits) - vis := make([]bool, 10) - ans := 0 - for i := 1; i < m; i++ { - ans += 9 * A(9, i-1) - } - for i := m - 1; i >= 0; i-- { - v := digits[i] - j := 0 - if i == m-1 { - j = 1 - } - for ; j < v; j++ { - if !vis[j] { - ans += A(10-(m-i), i) - } - } - if vis[v] { - break - } - vis[v] = true - if i == 0 { - ans++ - } - } - return ans -} - -func A(m, n int) int { - if n == 0 { - return 1 - } - return A(m, n-1) * (m - n + 1) -} -``` +The function executes as follows: -#### TypeScript +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$. -```ts -function numDupDigitsAtMostN(n: number): number { - return n - f(n); -} +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$. -function f(n: number): number { - const nums: number[] = []; - let i = -1; - for (; n; n = Math.floor(n / 10)) { - nums[++i] = n % 10; - } - const dp = Array.from({ length: 11 }, () => Array(1 << 11).fill(-1)); - const dfs = (pos: number, mask: number, lead: boolean, limit: boolean): number => { - if (pos < 0) { - return lead ? 0 : 1; - } - if (!lead && !limit && dp[pos][mask] !== -1) { - return dp[pos][mask]; - } - const up = limit ? nums[pos] : 9; - let ans = 0; - for (let i = 0; i <= up; ++i) { - if ((mask >> i) & 1) { - continue; - } - if (lead && i === 0) { - ans += dfs(pos - 1, mask, lead, limit && i === up); - } else { - ans += dfs(pos - 1, mask | (1 << i), false, limit && i === up); - } - } - if (!lead && !limit) { - dp[pos][mask] = ans; - } - return ans; - }; - return dfs(i, 0, true, true); -} -``` +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, \textit{mask}, \text{true}, \textit{limit} \wedge j = \textit{up})$. Otherwise, if the $j$-th bit of $\textit{mask}$ is $0$, we recursively calculate $\textit{dfs}(i + 1, \textit{mask} \,|\, 2^j, \text{false}, \textit{limit} \wedge j = \textit{up})$. We accumulate all the results as the answer. - +The answer is $n - \textit{dfs}(0, 0, \text{true}, \text{true})$. - +The time complexity is $O(\log n \times 2^D \times D)$, and the space complexity is $O(\log n \times 2^D)$. Here, $D = 10$. - +Similar problems: -### Solution 2 +- [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) +- [902. Numbers At Most N Given Digit Set](https://github.com/doocs/leetcode/blob/main/solution/0900-0999/0902.Numbers%20At%20Most%20N%20Given%20Digit%20Set/README_EN.md) +- [2376. Count Special Integers](https://github.com/doocs/leetcode/blob/main/solution/2300-2399/2376.Count%20Special%20Integers/README_EN.md) @@ -290,71 +103,54 @@ function f(n: number): number { ```python class Solution: def numDupDigitsAtMostN(self, n: int) -> int: - return n - self.f(n) - - def f(self, n: int) -> int: @cache - def dfs(pos: int, mask: int, lead: bool, limit: bool) -> int: - if pos < 0: - return int(lead) ^ 1 - up = nums[pos] if limit else 9 + def dfs(i: int, mask: int, lead: bool, 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 mask >> i & 1: - continue - if i == 0 and lead: - ans += dfs(pos - 1, mask, lead, limit and i == up) - else: - ans += dfs(pos - 1, mask | 1 << i, False, limit and i == up) + for j in range(up + 1): + if lead and j == 0: + ans += dfs(i + 1, mask, True, False) + elif mask >> j & 1 ^ 1: + ans += dfs(i + 1, mask | 1 << j, False, limit and j == up) return ans - nums = [] - while n: - nums.append(n % 10) - n //= 10 - return dfs(len(nums) - 1, 0, True, True) + s = str(n) + return n - dfs(0, 0, True, True) ``` #### Java ```java class Solution { - private int[] nums = new int[11]; - private Integer[][] dp = new Integer[11][1 << 11]; + private char[] s; + private Integer[][] f; public int numDupDigitsAtMostN(int n) { - return n - f(n); + s = String.valueOf(n).toCharArray(); + f = new Integer[s.length][1 << 10]; + return n - dfs(0, 0, true, true); } - private int f(int n) { - int i = -1; - for (; n > 0; n /= 10) { - nums[++i] = n % 10; - } - return dfs(i, 0, true, true); - } - - private int dfs(int pos, int mask, boolean lead, boolean limit) { - if (pos < 0) { + private int dfs(int i, int mask, boolean lead, boolean limit) { + if (i >= s.length) { return lead ? 0 : 1; } - if (!lead && !limit && dp[pos][mask] != null) { - return dp[pos][mask]; + if (!lead && !limit && f[i][mask] != null) { + return f[i][mask]; } + int up = limit ? s[i] - '0' : 9; int ans = 0; - int up = limit ? nums[pos] : 9; - for (int i = 0; i <= up; ++i) { - if ((mask >> i & 1) == 1) { - continue; - } - if (i == 0 && lead) { - ans += dfs(pos - 1, mask, lead, limit && i == up); - } else { - ans += dfs(pos - 1, mask | 1 << i, false, limit && i == up); + for (int j = 0; j <= up; ++j) { + if (lead && j == 0) { + ans += dfs(i + 1, mask, true, false); + } else if ((mask >> j & 1) == 0) { + ans += dfs(i + 1, mask | 1 << j, false, limit && j == up); } } if (!lead && !limit) { - dp[pos][mask] = ans; + f[i][mask] = ans; } return ans; } @@ -367,45 +163,32 @@ class Solution { class Solution { public: int numDupDigitsAtMostN(int n) { - return n - f(n); - } - -private: - int nums[11]; - int dp[11][1 << 11]; - - int f(int n) { - memset(dp, -1, sizeof(dp)); - int i = -1; - for (; n; n /= 10) { - nums[++i] = n % 10; - } - return dfs(i, 0, true, true); - } - - int dfs(int pos, int mask, bool lead, bool limit) { - if (pos < 0) { - return lead ? 0 : 1; - } - if (!lead && !limit && dp[pos][mask] != -1) { - return dp[pos][mask]; - } - int up = limit ? nums[pos] : 9; - int ans = 0; - for (int i = 0; i <= up; ++i) { - if (mask >> i & 1) { - continue; + string s = to_string(n); + int m = s.size(); + int f[m][1 << 10]; + memset(f, -1, sizeof(f)); + auto dfs = [&](auto&& dfs, int i, int mask, bool lead, bool limit) -> int { + if (i >= m) { + return lead ^ 1; } - if (i == 0 && lead) { - ans += dfs(pos - 1, mask, lead, limit && i == up); - } else { - ans += dfs(pos - 1, mask | 1 << i, false, limit && i == up); + if (!lead && !limit && f[i][mask] != -1) { + return f[i][mask]; } - } - if (!lead && !limit) { - dp[pos][mask] = ans; - } - return ans; + int up = limit ? s[i] - '0' : 9; + int ans = 0; + for (int j = 0; j <= up; ++j) { + if (lead && j == 0) { + ans += dfs(dfs, i + 1, mask, true, limit && j == up); + } else if (mask >> j & 1 ^ 1) { + ans += dfs(dfs, i + 1, mask | (1 << j), false, limit && j == up); + } + } + if (!lead && !limit) { + f[i][mask] = ans; + } + return ans; + }; + return n - dfs(dfs, 0, 0, true, true); } }; ``` @@ -414,52 +197,79 @@ private: ```go func numDupDigitsAtMostN(n int) int { - return n - f(n) -} - -func f(n int) int { - nums := []int{} - for ; n > 0; n /= 10 { - nums = append(nums, n%10) - } - dp := [11][1 << 11]int{} - for i := range dp { - for j := range dp[i] { - dp[i][j] = -1 + s := []byte(strconv.Itoa(n)) + m := len(s) + f := make([][]int, m) + for i := range f { + f[i] = make([]int, 1<<10) + for j := range f[i] { + f[i][j] = -1 } } - var dfs func(int, int, bool, bool) int - dfs = func(pos int, mask int, lead bool, limit bool) int { - if pos < 0 { + + var dfs func(i, mask int, lead, limit bool) int + dfs = func(i, mask int, lead, limit bool) int { + if i >= m { if lead { return 0 } return 1 } - if !lead && !limit && dp[pos][mask] != -1 { - return dp[pos][mask] + if !lead && !limit && f[i][mask] != -1 { + return f[i][mask] } up := 9 if limit { - up = nums[pos] + up = int(s[i] - '0') } ans := 0 - for i := 0; i <= up; i++ { - if mask>>i&1 == 1 { - continue - } - if i == 0 && lead { - ans += dfs(pos-1, mask, lead, limit && i == up) - } else { - ans += dfs(pos-1, mask|1<>j&1 == 0 { + ans += dfs(i+1, mask|(1< Array(1 << 10).fill(-1)); + + const dfs = (i: number, mask: number, lead: boolean, limit: boolean): number => { + if (i >= m) { + return lead ? 0 : 1; + } + if (!lead && !limit && f[i][mask] !== -1) { + return f[i][mask]; + } + const up = limit ? parseInt(s[i]) : 9; + let ans = 0; + for (let j = 0; j <= up; j++) { + if (lead && j === 0) { + ans += dfs(i + 1, mask, true, limit && j === up); + } else if (((mask >> j) & 1) === 0) { + ans += dfs(i + 1, mask | (1 << j), false, limit && j === up); + } + } + if (!lead && !limit) { + f[i][mask] = ans; + } + return ans; + }; + + return n - dfs(0, 0, true, true); } ``` diff --git a/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.cpp b/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.cpp index c312610d52eb2..332d9b47cb828 100644 --- a/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.cpp +++ b/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.cpp @@ -1,40 +1,31 @@ class Solution { public: int numDupDigitsAtMostN(int n) { - return n - f(n); - } - - int f(int n) { - int ans = 0; - vector digits; - while (n) { - digits.push_back(n % 10); - n /= 10; - } - int m = digits.size(); - vector vis(10); - for (int i = 1; i < m; ++i) { - ans += 9 * A(9, i - 1); - } - for (int i = m - 1; ~i; --i) { - int v = digits[i]; - for (int j = i == m - 1 ? 1 : 0; j < v; ++j) { - if (!vis[j]) { - ans += A(10 - (m - i), i); - } + string s = to_string(n); + int m = s.size(); + int f[m][1 << 10]; + memset(f, -1, sizeof(f)); + auto dfs = [&](auto&& dfs, int i, int mask, bool lead, bool limit) -> int { + if (i >= m) { + return lead ^ 1; } - if (vis[v]) { - break; + if (!lead && !limit && f[i][mask] != -1) { + return f[i][mask]; } - vis[v] = true; - if (i == 0) { - ++ans; + int up = limit ? s[i] - '0' : 9; + int ans = 0; + for (int j = 0; j <= up; ++j) { + if (lead && j == 0) { + ans += dfs(dfs, i + 1, mask, true, limit && j == up); + } else if (mask >> j & 1 ^ 1) { + ans += dfs(dfs, i + 1, mask | (1 << j), false, limit && j == up); + } } - } - return ans; - } - - int A(int m, int n) { - return n == 0 ? 1 : A(m, n - 1) * (m - n + 1); + if (!lead && !limit) { + f[i][mask] = ans; + } + return ans; + }; + return n - dfs(dfs, 0, 0, true, true); } }; \ No newline at end of file diff --git a/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.go b/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.go index a33eb792bc460..50661b0d38794 100644 --- a/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.go +++ b/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.go @@ -1,44 +1,41 @@ func numDupDigitsAtMostN(n int) int { - return n - f(n) -} - -func f(n int) int { - digits := []int{} - for n != 0 { - digits = append(digits, n%10) - n /= 10 - } - m := len(digits) - vis := make([]bool, 10) - ans := 0 - for i := 1; i < m; i++ { - ans += 9 * A(9, i-1) - } - for i := m - 1; i >= 0; i-- { - v := digits[i] - j := 0 - if i == m-1 { - j = 1 + s := []byte(strconv.Itoa(n)) + m := len(s) + f := make([][]int, m) + for i := range f { + f[i] = make([]int, 1<<10) + for j := range f[i] { + f[i][j] = -1 } - for ; j < v; j++ { - if !vis[j] { - ans += A(10-(m-i), i) + } + + var dfs func(i, mask int, lead, limit bool) int + dfs = func(i, mask int, lead, limit bool) int { + if i >= m { + if lead { + return 0 } + return 1 } - if vis[v] { - break + if !lead && !limit && f[i][mask] != -1 { + return f[i][mask] } - vis[v] = true - if i == 0 { - ans++ + up := 9 + if limit { + up = int(s[i] - '0') } + ans := 0 + for j := 0; j <= up; j++ { + if lead && j == 0 { + ans += dfs(i+1, mask, true, limit && j == up) + } else if mask>>j&1 == 0 { + ans += dfs(i+1, mask|(1< digits = new ArrayList<>(); - while (n != 0) { - digits.add(n % 10); - n /= 10; + private int dfs(int i, int mask, boolean lead, boolean limit) { + if (i >= s.length) { + return lead ? 0 : 1; } - int m = digits.size(); - int ans = 0; - for (int i = 1; i < m; ++i) { - ans += 9 * A(9, i - 1); + if (!lead && !limit && f[i][mask] != null) { + return f[i][mask]; } - boolean[] vis = new boolean[10]; - for (int i = m - 1; i >= 0; --i) { - int v = digits.get(i); - for (int j = i == m - 1 ? 1 : 0; j < v; ++j) { - if (vis[j]) { - continue; - } - ans += A(10 - (m - i), i); - } - if (vis[v]) { - break; - } - vis[v] = true; - if (i == 0) { - ++ans; + int up = limit ? s[i] - '0' : 9; + int ans = 0; + for (int j = 0; j <= up; ++j) { + if (lead && j == 0) { + ans += dfs(i + 1, mask, true, false); + } else if ((mask >> j & 1) == 0) { + ans += dfs(i + 1, mask | 1 << j, false, limit && j == up); } } + if (!lead && !limit) { + f[i][mask] = ans; + } return ans; } - - private int A(int m, int n) { - return n == 0 ? 1 : A(m, n - 1) * (m - n + 1); - } } \ No newline at end of file diff --git a/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.py b/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.py index 76dc654b17e41..14a99e986b934 100644 --- a/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.py +++ b/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.py @@ -1,27 +1,17 @@ class Solution: def numDupDigitsAtMostN(self, n: int) -> int: - return n - self.f(n) + @cache + def dfs(i: int, mask: int, lead: bool, limit: bool) -> int: + if i >= len(s): + return lead ^ 1 + up = int(s[i]) if limit else 9 + ans = 0 + for j in range(up + 1): + if lead and j == 0: + ans += dfs(i + 1, mask, True, False) + elif mask >> j & 1 ^ 1: + ans += dfs(i + 1, mask | 1 << j, False, limit and j == up) + return ans - def f(self, n): - def A(m, n): - return 1 if n == 0 else A(m, n - 1) * (m - n + 1) - - vis = [False] * 10 - ans = 0 - digits = [int(c) for c in str(n)[::-1]] - m = len(digits) - for i in range(1, m): - ans += 9 * A(9, i - 1) - for i in range(m - 1, -1, -1): - v = digits[i] - j = 1 if i == m - 1 else 0 - while j < v: - if not vis[j]: - ans += A(10 - (m - i), i) - j += 1 - if vis[v]: - break - vis[v] = True - if i == 0: - ans += 1 - return ans + s = str(n) + return n - dfs(0, 0, True, True) diff --git a/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.ts b/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.ts index ef558b76ebbfe..b5e49884a61e7 100644 --- a/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.ts +++ b/solution/1000-1099/1012.Numbers With Repeated Digits/Solution.ts @@ -1,37 +1,29 @@ function numDupDigitsAtMostN(n: number): number { - return n - f(n); -} + const s = n.toString(); + const m = s.length; + const f = Array.from({ length: m }, () => Array(1 << 10).fill(-1)); -function f(n: number): number { - const nums: number[] = []; - let i = -1; - for (; n; n = Math.floor(n / 10)) { - nums[++i] = n % 10; - } - const dp = Array.from({ length: 11 }, () => Array(1 << 11).fill(-1)); - const dfs = (pos: number, mask: number, lead: boolean, limit: boolean): number => { - if (pos < 0) { + const dfs = (i: number, mask: number, lead: boolean, limit: boolean): number => { + if (i >= m) { return lead ? 0 : 1; } - if (!lead && !limit && dp[pos][mask] !== -1) { - return dp[pos][mask]; + if (!lead && !limit && f[i][mask] !== -1) { + return f[i][mask]; } - const up = limit ? nums[pos] : 9; + const up = limit ? parseInt(s[i]) : 9; let ans = 0; - for (let i = 0; i <= up; ++i) { - if ((mask >> i) & 1) { - continue; - } - if (lead && i === 0) { - ans += dfs(pos - 1, mask, lead, limit && i === up); - } else { - ans += dfs(pos - 1, mask | (1 << i), false, limit && i === up); + for (let j = 0; j <= up; j++) { + if (lead && j === 0) { + ans += dfs(i + 1, mask, true, limit && j === up); + } else if (((mask >> j) & 1) === 0) { + ans += dfs(i + 1, mask | (1 << j), false, limit && j === up); } } if (!lead && !limit) { - dp[pos][mask] = ans; + f[i][mask] = ans; } return ans; }; - return dfs(i, 0, true, true); + + return n - dfs(0, 0, true, true); } diff --git a/solution/1000-1099/1012.Numbers With Repeated Digits/Solution2.cpp b/solution/1000-1099/1012.Numbers With Repeated Digits/Solution2.cpp deleted file mode 100644 index 6c97091af703b..0000000000000 --- a/solution/1000-1099/1012.Numbers With Repeated Digits/Solution2.cpp +++ /dev/null @@ -1,44 +0,0 @@ -class Solution { -public: - int numDupDigitsAtMostN(int n) { - return n - f(n); - } - -private: - int nums[11]; - int dp[11][1 << 11]; - - int f(int n) { - memset(dp, -1, sizeof(dp)); - int i = -1; - for (; n; n /= 10) { - nums[++i] = n % 10; - } - return dfs(i, 0, true, true); - } - - int dfs(int pos, int mask, bool lead, bool limit) { - if (pos < 0) { - return lead ? 0 : 1; - } - if (!lead && !limit && dp[pos][mask] != -1) { - return dp[pos][mask]; - } - int up = limit ? nums[pos] : 9; - int ans = 0; - for (int i = 0; i <= up; ++i) { - if (mask >> i & 1) { - continue; - } - if (i == 0 && lead) { - ans += dfs(pos - 1, mask, lead, limit && i == up); - } else { - ans += dfs(pos - 1, mask | 1 << i, false, limit && i == up); - } - } - if (!lead && !limit) { - dp[pos][mask] = ans; - } - return ans; - } -}; \ No newline at end of file diff --git a/solution/1000-1099/1012.Numbers With Repeated Digits/Solution2.go b/solution/1000-1099/1012.Numbers With Repeated Digits/Solution2.go deleted file mode 100644 index 31704f2ebb67d..0000000000000 --- a/solution/1000-1099/1012.Numbers With Repeated Digits/Solution2.go +++ /dev/null @@ -1,48 +0,0 @@ -func numDupDigitsAtMostN(n int) int { - return n - f(n) -} - -func f(n int) int { - nums := []int{} - for ; n > 0; n /= 10 { - nums = append(nums, n%10) - } - dp := [11][1 << 11]int{} - for i := range dp { - for j := range dp[i] { - dp[i][j] = -1 - } - } - var dfs func(int, int, bool, bool) int - dfs = func(pos int, mask int, lead bool, limit bool) int { - if pos < 0 { - if lead { - return 0 - } - return 1 - } - if !lead && !limit && dp[pos][mask] != -1 { - return dp[pos][mask] - } - up := 9 - if limit { - up = nums[pos] - } - ans := 0 - for i := 0; i <= up; i++ { - if mask>>i&1 == 1 { - continue - } - if i == 0 && lead { - ans += dfs(pos-1, mask, lead, limit && i == up) - } else { - ans += dfs(pos-1, mask|1< 0; n /= 10) { - nums[++i] = n % 10; - } - return dfs(i, 0, true, true); - } - - private int dfs(int pos, int mask, boolean lead, boolean limit) { - if (pos < 0) { - return lead ? 0 : 1; - } - if (!lead && !limit && dp[pos][mask] != null) { - return dp[pos][mask]; - } - int ans = 0; - int up = limit ? nums[pos] : 9; - for (int i = 0; i <= up; ++i) { - if ((mask >> i & 1) == 1) { - continue; - } - if (i == 0 && lead) { - ans += dfs(pos - 1, mask, lead, limit && i == up); - } else { - ans += dfs(pos - 1, mask | 1 << i, false, limit && i == up); - } - } - if (!lead && !limit) { - dp[pos][mask] = ans; - } - return ans; - } -} \ No newline at end of file diff --git a/solution/1000-1099/1012.Numbers With Repeated Digits/Solution2.py b/solution/1000-1099/1012.Numbers With Repeated Digits/Solution2.py deleted file mode 100644 index 2f9732635eb58..0000000000000 --- a/solution/1000-1099/1012.Numbers With Repeated Digits/Solution2.py +++ /dev/null @@ -1,25 +0,0 @@ -class Solution: - def numDupDigitsAtMostN(self, n: int) -> int: - return n - self.f(n) - - def f(self, n: int) -> int: - @cache - def dfs(pos: int, mask: int, lead: bool, limit: bool) -> int: - if pos < 0: - return int(lead) ^ 1 - up = nums[pos] if limit else 9 - ans = 0 - for i in range(up + 1): - if mask >> i & 1: - continue - if i == 0 and lead: - ans += dfs(pos - 1, mask, lead, limit and i == up) - else: - ans += dfs(pos - 1, mask | 1 << i, False, limit and i == up) - return ans - - nums = [] - while n: - nums.append(n % 10) - n //= 10 - return dfs(len(nums) - 1, 0, True, True)