From c6b2eb5f35bb18860686aa39a727e295c4654153 Mon Sep 17 00:00:00 2001 From: Yang Libin Date: Fri, 9 Feb 2024 13:51:31 +0000 Subject: [PATCH] feat: add solutions to lc problem: No.3032 No.3032.Count Numbers With Unique Digits II --- .../README.md | 195 +++++++++++++++++- .../README_EN.md | 195 +++++++++++++++++- .../Solution.cpp | 35 ++++ .../Solution.go | 49 +++++ .../Solution.java | 36 ++++ .../Solution.py | 21 ++ .../Solution.ts | 36 ++++ 7 files changed, 565 insertions(+), 2 deletions(-) create mode 100644 solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.cpp create mode 100644 solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.go create mode 100644 solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.java create mode 100644 solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.py create mode 100644 solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.ts diff --git a/solution/3000-3099/3032.Count Numbers With Unique Digits II/README.md b/solution/3000-3099/3032.Count Numbers With Unique Digits II/README.md index 9a757131cdbf9..9926c4ce2d5d2 100644 --- a/solution/3000-3099/3032.Count Numbers With Unique Digits II/README.md +++ b/solution/3000-3099/3032.Count Numbers With Unique Digits II/README.md @@ -42,24 +42,217 @@ Given two positive integers a and b, ## 解法 -### 方法一 +### 方法一:状态压缩 + 数位 DP + +题目要求统计区间 $[a, b]$ 中的数中有多少个数的数位是唯一的,我们可以使用状态压缩和数位 DP 来解决这个问题。 + +我们可以用一个函数 $f(n)$ 来统计 $[1, n]$ 中的数中有多少个数的数位是唯一的,那么答案就是 $f(b) - f(a - 1)$。 + +另外,我们可以用一个二进制数来记录数字中出现过的数字,比如数字中出现了 $1, 3, 5$,那么我们可以用 $10101$ 来表示这个状态。 + +接下来,我们使用记忆化搜索来实现数位 DP。从起点向下搜索,到最底层得到方案数,一层层向上返回答案并累加,最后从搜索起点得到最终的答案。 + +基本步骤如下: + +1. 我们将数字 $n$ 转换为字符串 $num$,其中 $num[0]$ 为最高位,而 $num[len - 1]$ 为最低位。 +2. 根据题目信息,设计一个函数 $dfs(pos, mask, limit)$,其中 $pos$ 表示当前处理的位置,$mask$ 表示当前数字中出现过的数字,$limit$ 表示当前位置是否有限制。如果 $limit$ 为真,那么当前位置的数字不能超过 $num[pos]$。 + +答案为 $dfs(0, 0, true)$。 + +时间复杂度 $O(m \times 2^{10} \times 10)$,空间复杂度 $O(m \times 2^{10})$。其中 $m$ 为 $b$ 的位数。 ```python +class Solution: + def numberCount(self, a: int, b: int) -> int: + @cache + def dfs(pos: int, mask: int, limit: bool) -> int: + if pos >= len(num): + return 1 if mask else 0 + up = int(num[pos]) if limit else 9 + ans = 0 + for i in range(up + 1): + if mask >> i & 1: + continue + nxt = 0 if mask == 0 and i == 0 else mask | 1 << i + ans += dfs(pos + 1, nxt, limit and i == up) + return ans + num = str(a - 1) + x = dfs(0, 0, True) + dfs.cache_clear() + num = str(b) + y = dfs(0, 0, True) + return y - x ``` ```java +class Solution { + private String num; + private Integer[][] f; + + public int numberCount(int a, int b) { + num = String.valueOf(a - 1); + f = new Integer[num.length()][1 << 10]; + int x = dfs(0, 0, true); + num = String.valueOf(b); + f = new Integer[num.length()][1 << 10]; + int y = dfs(0, 0, true); + return y - x; + } + private int dfs(int pos, int mask, boolean limit) { + if (pos >= num.length()) { + return mask > 0 ? 1 : 0; + } + if (!limit && f[pos][mask] != null) { + return f[pos][mask]; + } + int up = limit ? num.charAt(pos) - '0' : 9; + int ans = 0; + for (int i = 0; i <= up; ++i) { + if ((mask >> i & 1) == 1) { + continue; + } + int nxt = mask == 0 && i == 0 ? 0 : mask | 1 << i; + ans += dfs(pos + 1, nxt, limit && i == up); + } + if (!limit) { + f[pos][mask] = ans; + } + return ans; + } +} ``` ```cpp +class Solution { +public: + int numberCount(int a, int b) { + string num = to_string(b); + int f[num.size()][1 << 10]; + memset(f, -1, sizeof(f)); + function dfs = [&](int pos, int mask, bool limit) { + if (pos >= num.size()) { + return mask ? 1 : 0; + } + if (!limit && f[pos][mask] != -1) { + return f[pos][mask]; + } + int up = limit ? num[pos] - '0' : 9; + int ans = 0; + for (int i = 0; i <= up; ++i) { + if (mask >> i & 1) { + continue; + } + int nxt = mask == 0 && i == 0 ? 0 : mask | 1 << i; + ans += dfs(pos + 1, nxt, limit && i == up); + } + if (!limit) { + f[pos][mask] = ans; + } + return ans; + }; + int y = dfs(0, 0, true); + num = to_string(a - 1); + memset(f, -1, sizeof(f)); + int x = dfs(0, 0, true); + return y - x; + } +}; ``` ```go +func numberCount(a int, b int) int { + num := strconv.Itoa(b) + f := make([][1 << 10]int, len(num)) + for i := range f { + for j := range f[i] { + f[i][j] = -1 + } + } + var dfs func(pos, mask int, limit bool) int + dfs = func(pos, mask int, limit bool) int { + if pos >= len(num) { + if mask != 0 { + return 1 + } + return 0 + } + if !limit && f[pos][mask] != -1 { + return f[pos][mask] + } + up := 9 + if limit { + up = int(num[pos] - '0') + } + ans := 0 + for i := 0; i <= up; i++ { + if mask>>i&1 == 1 { + continue + } + nxt := mask | 1< Array(1 << 10).fill(-1)); + const dfs: (pos: number, mask: number, limit: boolean) => number = (pos, mask, limit) => { + if (pos >= num.length) { + return mask ? 1 : 0; + } + if (!limit && f[pos][mask] !== -1) { + return f[pos][mask]; + } + const up: number = limit ? +num[pos] : 9; + let ans: number = 0; + for (let i = 0; i <= up; i++) { + if ((mask >> i) & 1) { + continue; + } + let nxt: number = mask | (1 << i); + if (mask === 0 && i === 0) { + nxt = 0; + } + ans += dfs(pos + 1, nxt, limit && i === up); + } + if (!limit) { + f[pos][mask] = ans; + } + return ans; + }; + const y: number = dfs(0, 0, true); + num = (a - 1).toString(); + f.forEach(v => v.fill(-1)); + const x: number = dfs(0, 0, true); + return y - x; +} ``` diff --git a/solution/3000-3099/3032.Count Numbers With Unique Digits II/README_EN.md b/solution/3000-3099/3032.Count Numbers With Unique Digits II/README_EN.md index b98cf15652084..4e3b61117b093 100644 --- a/solution/3000-3099/3032.Count Numbers With Unique Digits II/README_EN.md +++ b/solution/3000-3099/3032.Count Numbers With Unique Digits II/README_EN.md @@ -40,24 +40,217 @@ Given two positive integers a and b, ## Solutions -### Solution 1 +### Solution 1: State Compression + Digit DP + +The problem asks to count how many numbers in the range $[a, b]$ have unique digits. We can solve this problem using state compression and digit DP. + +We can use a function $f(n)$ to count how many numbers in the range $[1, n]$ have unique digits. Then the answer is $f(b) - f(a - 1)$. + +In addition, we can use a binary number to record the digits that have appeared in the number. For example, if the digits $1, 3, 5$ have appeared in the number, we can use $10101$ to represent this state. + +Next, 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 search starting point. + +The basic steps are as follows: + +1. We convert the number $n$ into a string $num$, where $num[0]$ is the highest digit and $num[len - 1]$ is the lowest digit. +2. Based on the problem information, we design a function $dfs(pos, mask, limit)$, where $pos$ represents the current processing position, $mask$ represents the digits that have appeared in the current number, and $limit$ represents whether there is a limit at the current position. If $limit$ is true, then the digit at the current position cannot exceed $num[pos]$. + +The answer is $dfs(0, 0, true)$. + +The time complexity is $O(m \times 2^{10} \times 10)$, and the space complexity is $O(m \times 2^{10})$. Where $m$ is the number of digits in $b$. ```python +class Solution: + def numberCount(self, a: int, b: int) -> int: + @cache + def dfs(pos: int, mask: int, limit: bool) -> int: + if pos >= len(num): + return 1 if mask else 0 + up = int(num[pos]) if limit else 9 + ans = 0 + for i in range(up + 1): + if mask >> i & 1: + continue + nxt = 0 if mask == 0 and i == 0 else mask | 1 << i + ans += dfs(pos + 1, nxt, limit and i == up) + return ans + num = str(a - 1) + x = dfs(0, 0, True) + dfs.cache_clear() + num = str(b) + y = dfs(0, 0, True) + return y - x ``` ```java +class Solution { + private String num; + private Integer[][] f; + + public int numberCount(int a, int b) { + num = String.valueOf(a - 1); + f = new Integer[num.length()][1 << 10]; + int x = dfs(0, 0, true); + num = String.valueOf(b); + f = new Integer[num.length()][1 << 10]; + int y = dfs(0, 0, true); + return y - x; + } + private int dfs(int pos, int mask, boolean limit) { + if (pos >= num.length()) { + return mask > 0 ? 1 : 0; + } + if (!limit && f[pos][mask] != null) { + return f[pos][mask]; + } + int up = limit ? num.charAt(pos) - '0' : 9; + int ans = 0; + for (int i = 0; i <= up; ++i) { + if ((mask >> i & 1) == 1) { + continue; + } + int nxt = mask == 0 && i == 0 ? 0 : mask | 1 << i; + ans += dfs(pos + 1, nxt, limit && i == up); + } + if (!limit) { + f[pos][mask] = ans; + } + return ans; + } +} ``` ```cpp +class Solution { +public: + int numberCount(int a, int b) { + string num = to_string(b); + int f[num.size()][1 << 10]; + memset(f, -1, sizeof(f)); + function dfs = [&](int pos, int mask, bool limit) { + if (pos >= num.size()) { + return mask ? 1 : 0; + } + if (!limit && f[pos][mask] != -1) { + return f[pos][mask]; + } + int up = limit ? num[pos] - '0' : 9; + int ans = 0; + for (int i = 0; i <= up; ++i) { + if (mask >> i & 1) { + continue; + } + int nxt = mask == 0 && i == 0 ? 0 : mask | 1 << i; + ans += dfs(pos + 1, nxt, limit && i == up); + } + if (!limit) { + f[pos][mask] = ans; + } + return ans; + }; + int y = dfs(0, 0, true); + num = to_string(a - 1); + memset(f, -1, sizeof(f)); + int x = dfs(0, 0, true); + return y - x; + } +}; ``` ```go +func numberCount(a int, b int) int { + num := strconv.Itoa(b) + f := make([][1 << 10]int, len(num)) + for i := range f { + for j := range f[i] { + f[i][j] = -1 + } + } + var dfs func(pos, mask int, limit bool) int + dfs = func(pos, mask int, limit bool) int { + if pos >= len(num) { + if mask != 0 { + return 1 + } + return 0 + } + if !limit && f[pos][mask] != -1 { + return f[pos][mask] + } + up := 9 + if limit { + up = int(num[pos] - '0') + } + ans := 0 + for i := 0; i <= up; i++ { + if mask>>i&1 == 1 { + continue + } + nxt := mask | 1< Array(1 << 10).fill(-1)); + const dfs: (pos: number, mask: number, limit: boolean) => number = (pos, mask, limit) => { + if (pos >= num.length) { + return mask ? 1 : 0; + } + if (!limit && f[pos][mask] !== -1) { + return f[pos][mask]; + } + const up: number = limit ? +num[pos] : 9; + let ans: number = 0; + for (let i = 0; i <= up; i++) { + if ((mask >> i) & 1) { + continue; + } + let nxt: number = mask | (1 << i); + if (mask === 0 && i === 0) { + nxt = 0; + } + ans += dfs(pos + 1, nxt, limit && i === up); + } + if (!limit) { + f[pos][mask] = ans; + } + return ans; + }; + const y: number = dfs(0, 0, true); + num = (a - 1).toString(); + f.forEach(v => v.fill(-1)); + const x: number = dfs(0, 0, true); + return y - x; +} ``` diff --git a/solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.cpp b/solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.cpp new file mode 100644 index 0000000000000..47f745794fc90 --- /dev/null +++ b/solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.cpp @@ -0,0 +1,35 @@ +class Solution { +public: + int numberCount(int a, int b) { + string num = to_string(b); + int f[num.size()][1 << 10]; + memset(f, -1, sizeof(f)); + function dfs = [&](int pos, int mask, bool limit) { + if (pos >= num.size()) { + return mask ? 1 : 0; + } + if (!limit && f[pos][mask] != -1) { + return f[pos][mask]; + } + int up = limit ? num[pos] - '0' : 9; + int ans = 0; + for (int i = 0; i <= up; ++i) { + if (mask >> i & 1) { + continue; + } + int nxt = mask == 0 && i == 0 ? 0 : mask | 1 << i; + ans += dfs(pos + 1, nxt, limit && i == up); + } + if (!limit) { + f[pos][mask] = ans; + } + return ans; + }; + + int y = dfs(0, 0, true); + num = to_string(a - 1); + memset(f, -1, sizeof(f)); + int x = dfs(0, 0, true); + return y - x; + } +}; \ No newline at end of file diff --git a/solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.go b/solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.go new file mode 100644 index 0000000000000..6e8c0bb3b3fa5 --- /dev/null +++ b/solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.go @@ -0,0 +1,49 @@ +func numberCount(a int, b int) int { + num := strconv.Itoa(b) + f := make([][1 << 10]int, len(num)) + for i := range f { + for j := range f[i] { + f[i][j] = -1 + } + } + var dfs func(pos, mask int, limit bool) int + dfs = func(pos, mask int, limit bool) int { + if pos >= len(num) { + if mask != 0 { + return 1 + } + return 0 + } + if !limit && f[pos][mask] != -1 { + return f[pos][mask] + } + up := 9 + if limit { + up = int(num[pos] - '0') + } + ans := 0 + for i := 0; i <= up; i++ { + if mask>>i&1 == 1 { + continue + } + nxt := mask | 1<= num.length()) { + return mask > 0 ? 1 : 0; + } + if (!limit && f[pos][mask] != null) { + return f[pos][mask]; + } + int up = limit ? num.charAt(pos) - '0' : 9; + int ans = 0; + for (int i = 0; i <= up; ++i) { + if ((mask >> i & 1) == 1) { + continue; + } + int nxt = mask == 0 && i == 0 ? 0 : mask | 1 << i; + ans += dfs(pos + 1, nxt, limit && i == up); + } + if (!limit) { + f[pos][mask] = ans; + } + return ans; + } +} \ No newline at end of file diff --git a/solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.py b/solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.py new file mode 100644 index 0000000000000..2b3b29b34a550 --- /dev/null +++ b/solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.py @@ -0,0 +1,21 @@ +class Solution: + def numberCount(self, a: int, b: int) -> int: + @cache + def dfs(pos: int, mask: int, limit: bool) -> int: + if pos >= len(num): + return 1 if mask else 0 + up = int(num[pos]) if limit else 9 + ans = 0 + for i in range(up + 1): + if mask >> i & 1: + continue + nxt = 0 if mask == 0 and i == 0 else mask | 1 << i + ans += dfs(pos + 1, nxt, limit and i == up) + return ans + + num = str(a - 1) + x = dfs(0, 0, True) + dfs.cache_clear() + num = str(b) + y = dfs(0, 0, True) + return y - x diff --git a/solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.ts b/solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.ts new file mode 100644 index 0000000000000..7c2fb91a616ef --- /dev/null +++ b/solution/3000-3099/3032.Count Numbers With Unique Digits II/Solution.ts @@ -0,0 +1,36 @@ +function numberCount(a: number, b: number): number { + let num: string = b.toString(); + const f: number[][] = Array(num.length) + .fill(0) + .map(() => Array(1 << 10).fill(-1)); + const dfs: (pos: number, mask: number, limit: boolean) => number = (pos, mask, limit) => { + if (pos >= num.length) { + return mask ? 1 : 0; + } + if (!limit && f[pos][mask] !== -1) { + return f[pos][mask]; + } + const up: number = limit ? +num[pos] : 9; + let ans: number = 0; + for (let i = 0; i <= up; i++) { + if ((mask >> i) & 1) { + continue; + } + let nxt: number = mask | (1 << i); + if (mask === 0 && i === 0) { + nxt = 0; + } + ans += dfs(pos + 1, nxt, limit && i === up); + } + if (!limit) { + f[pos][mask] = ans; + } + return ans; + }; + + const y: number = dfs(0, 0, true); + num = (a - 1).toString(); + f.forEach(v => v.fill(-1)); + const x: number = dfs(0, 0, true); + return y - x; +}