Skip to content

Commit 9492cca

Browse files
authored
feat: add solutions to lc problem: No.0357 (#3540)
No.0357.Count Numbers with Unique Digits
1 parent 1b756af commit 9492cca

File tree

11 files changed

+360
-458
lines changed

11 files changed

+360
-458
lines changed

solution/0300-0399/0357.Count Numbers with Unique Digits/README.md

+113-167
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ tags:
2929
<pre>
3030
<strong>输入:</strong>n = 2
3131
<strong>输出:</strong>91
32-
<strong>解释:</strong>答案应为除去 <code>11、22、33、44、55、66、77、88、99 </code>外,在 0 ≤ x &lt; 100 范围内的所有数字。
32+
<strong>解释:</strong>答案应为除去 <code>11、22、33、44、55、66、77、88、99 </code>外,在 0 ≤ x &lt; 100 范围内的所有数字。
3333
</pre>
3434

3535
<p><strong>示例 2:</strong></p>
@@ -55,101 +55,7 @@ tags:
5555

5656
<!-- solution:start -->
5757

58-
### 方法一:排列组合
59-
60-
当 $n=0$ 时,有 $0\le x \lt 1$,只有 $1$ 个数字,即 $0$。
61-
62-
当 $n=1$ 时,有 $0\le x \lt 10$,有 $10$ 个数字,即 $0,1,2,3,4,5,6,7,8,9$。
63-
64-
当 $n=2$ 时,有 $0\le x \lt 100$,那么 $x$ 的选择可以由两部分组成:只有一位数的数字和有两位数的数字。对于只有一位数的情况,可以由上述的边界情况计算;对于有两位数的情况,由于第一位数字不能为 $0$,所以第一位数字有 $9$ 种选择,第二位数字有 $9$ 种选择,所以有 $9 \times 9$ 种选择,即 $81$ 种选择。
65-
66-
更一般的情况,含有 $n$ 位数且各位数字都不同的数字 $x$ 的个数为 $9 \times A_{9}^{n-1}$。再加上含有小于 $n$ 位数且各位数字都不同的数字 $x$ 的个数,即为答案。
67-
68-
时间复杂度 $O(n)$。
69-
70-
<!-- tabs:start -->
71-
72-
#### Python3
73-
74-
```python
75-
class Solution:
76-
def countNumbersWithUniqueDigits(self, n: int) -> int:
77-
if n == 0:
78-
return 1
79-
if n == 1:
80-
return 10
81-
ans, cur = 10, 9
82-
for i in range(n - 1):
83-
cur *= 9 - i
84-
ans += cur
85-
return ans
86-
```
87-
88-
#### Java
89-
90-
```java
91-
class Solution {
92-
public int countNumbersWithUniqueDigits(int n) {
93-
if (n == 0) {
94-
return 1;
95-
}
96-
if (n == 1) {
97-
return 10;
98-
}
99-
int ans = 10;
100-
for (int i = 0, cur = 9; i < n - 1; ++i) {
101-
cur *= (9 - i);
102-
ans += cur;
103-
}
104-
return ans;
105-
}
106-
}
107-
```
108-
109-
#### C++
110-
111-
```cpp
112-
class Solution {
113-
public:
114-
int countNumbersWithUniqueDigits(int n) {
115-
if (n == 0) return 1;
116-
if (n == 1) return 10;
117-
int ans = 10;
118-
for (int i = 0, cur = 9; i < n - 1; ++i) {
119-
cur *= (9 - i);
120-
ans += cur;
121-
}
122-
return ans;
123-
}
124-
};
125-
```
126-
127-
#### Go
128-
129-
```go
130-
func countNumbersWithUniqueDigits(n int) int {
131-
if n == 0 {
132-
return 1
133-
}
134-
if n == 1 {
135-
return 10
136-
}
137-
ans := 10
138-
for i, cur := 0, 9; i < n-1; i++ {
139-
cur *= (9 - i)
140-
ans += cur
141-
}
142-
return ans
143-
}
144-
```
145-
146-
<!-- tabs:end -->
147-
148-
<!-- solution:end -->
149-
150-
<!-- solution:start -->
151-
152-
### 方法二:状态压缩 + 数位 DP
58+
### 方法一:状态压缩 + 数位 DP
15359

15460
这道题实际上是求在给定区间 $[l,..r]$ 中,满足条件的数的个数。条件与数的大小无关,而只与数的组成有关,因此可以使用数位 DP 的思想求解。数位 DP 中,数的大小对复杂度的影响很小。
15561

@@ -163,17 +69,29 @@ $$
16369

16470
这里我们用记忆化搜索来实现数位 DP。从起点向下搜索,到最底层得到方案数,一层层向上返回答案并累加,最后从搜索起点得到最终的答案。
16571

166-
我们根据题目信息,设计函数 $dfs()$,对于本题,我们定义 $dfs(pos, mask, lead)$,答案为 $dfs(len, 0, true)$。
72+
我们根据题目信息,设计一个函数 $\textit{dfs}(i, \textit{mask}, \textit{lead})$,其中:
73+
74+
- 数字 $i$ 表示当前搜索到的位置,我们从高位开始搜索,即 $i = 0$ 表示最高位。
75+
- 数字 $\textit{mask}$ 表示当前数字的状态,即 $\textit{mask}$ 的第 $j$ 位为 $1$ 表示数字 $j$ 已经被使用过。
76+
- 布尔值 $\textit{lead}$ 表示当前是否只包含前导 $0$。
16777

168-
其中
78+
函数的执行过程如下
16979

170-
- `pos` 表示数字的位数,从末位或者第一位开始,一般根据题目的数字构造性质来选择顺序。对于本题,我们选择从高位开始,因此,`pos` 的初始值为 `len`
171-
- `mask` 表示当前数字选取了哪些数字(状态压缩);
172-
- `lead` 表示当前数字是否含有前导零;
80+
如果 $i$ 超过了数字 $n$ 的长度,即 $i \lt 0$,说明搜索结束,直接返回 $1$。
81+
82+
否则,我们从 $0$ 到 $9$ 枚举位置 $i$ 的数字 $j$,对于每一个 $j$:
83+
84+
- 如果 $\textit{mask}$ 的第 $j$ 位为 $1$,说明数字 $j$ 已经被使用过,直接跳过。
85+
- 如果 $\textit{lead}$ 为真且 $j = 0$,说明当前数字只包含前导 $0$,递归到下一层时,此时 $\textit{lead}$ 仍为真。
86+
- 否则,我们递归到下一层,更新 $\textit{mask}$ 的第 $j$ 位为 $1$,并将 $\textit{lead}$ 更新为假。
87+
88+
最后,我们将所有递归到下一层的结果累加,即为答案。
89+
90+
答案为 $\textit{dfs}(n - 1, 0, \textit{True})$。
17391

17492
关于函数的实现细节,可以参考下面的代码。
17593

176-
时间复杂度 $O(n)$。
94+
时间复杂度 $O(n \times 2^D \times D)$,空间复杂度 $O(n \times 2^D)$。其中 $n$ 为数字 $n$ 的长度,而 $D = 10$。
17795

17896
相似题目:
17997

@@ -192,55 +110,53 @@ $$
192110
class Solution:
193111
def countNumbersWithUniqueDigits(self, n: int) -> int:
194112
@cache
195-
def dfs(pos, mask, lead):
196-
if pos <= 0:
113+
def dfs(i: int, mask: int, lead: bool) -> int:
114+
if i < 0:
197115
return 1
198116
ans = 0
199-
for i in range(10):
200-
if (mask >> i) & 1:
117+
for j in range(10):
118+
if mask >> j & 1:
201119
continue
202-
if i == 0 and lead:
203-
ans += dfs(pos - 1, mask, lead)
120+
if lead and j == 0:
121+
ans += dfs(i - 1, mask, True)
204122
else:
205-
ans += dfs(pos - 1, mask | (1 << i), False)
123+
ans += dfs(i - 1, mask | 1 << j, False)
206124
return ans
207125

208-
return dfs(n, 0, True)
126+
return dfs(n - 1, 0, True)
209127
```
210128

211129
#### Java
212130

213131
```java
214132
class Solution {
215-
private int[][] dp = new int[10][1 << 11];
133+
private Integer[][] f;
216134

217135
public int countNumbersWithUniqueDigits(int n) {
218-
for (var e : dp) {
219-
Arrays.fill(e, -1);
220-
}
221-
return dfs(n, 0, true);
136+
f = new Integer[n][1 << 10];
137+
return dfs(n - 1, 0, true);
222138
}
223139

224-
private int dfs(int pos, int mask, boolean lead) {
225-
if (pos <= 0) {
140+
private int dfs(int i, int mask, boolean lead) {
141+
if (i < 0) {
226142
return 1;
227143
}
228-
if (!lead && dp[pos][mask] != -1) {
229-
return dp[pos][mask];
144+
if (!lead && f[i][mask] != null) {
145+
return f[i][mask];
230146
}
231147
int ans = 0;
232-
for (int i = 0; i < 10; ++i) {
233-
if (((mask >> i) & 1) == 1) {
148+
for (int j = 0; j <= 9; ++j) {
149+
if ((mask >> j & 1) == 1) {
234150
continue;
235151
}
236-
if (i == 0 && lead) {
237-
ans += dfs(pos - 1, mask, lead);
152+
if (lead && j == 0) {
153+
ans += dfs(i - 1, mask, true);
238154
} else {
239-
ans += dfs(pos - 1, mask | (1 << i), false);
155+
ans += dfs(i - 1, mask | 1 << j, false);
240156
}
241157
}
242158
if (!lead) {
243-
dp[pos][mask] = ans;
159+
f[i][mask] = ans;
244160
}
245161
return ans;
246162
}
@@ -252,33 +168,33 @@ class Solution {
252168
```cpp
253169
class Solution {
254170
public:
255-
int dp[10][1 << 11];
256-
257171
int countNumbersWithUniqueDigits(int n) {
258-
memset(dp, -1, sizeof dp);
259-
return dfs(n, 0, true);
260-
}
261-
262-
int dfs(int pos, int mask, bool lead) {
263-
if (pos <= 0) {
264-
return 1;
265-
}
266-
if (!lead && dp[pos][mask] != -1) {
267-
return dp[pos][mask];
268-
}
269-
int ans = 0;
270-
for (int i = 0; i < 10; ++i) {
271-
if ((mask >> i) & 1) continue;
272-
if (i == 0 && lead) {
273-
ans += dfs(pos - 1, mask, lead);
274-
} else {
275-
ans += dfs(pos - 1, mask | 1 << i, false);
172+
int f[n + 1][1 << 10];
173+
memset(f, -1, sizeof(f));
174+
auto dfs = [&](auto&& dfs, int i, int mask, bool lead) -> int {
175+
if (i < 0) {
176+
return 1;
276177
}
277-
}
278-
if (!lead) {
279-
dp[pos][mask] = ans;
280-
}
281-
return ans;
178+
if (!lead && f[i][mask] != -1) {
179+
return f[i][mask];
180+
}
181+
int ans = 0;
182+
for (int j = 0; j <= 9; ++j) {
183+
if (mask >> j & 1) {
184+
continue;
185+
}
186+
if (lead && j == 0) {
187+
ans += dfs(dfs, i - 1, mask, true);
188+
} else {
189+
ans += dfs(dfs, i - 1, mask | 1 << i, false);
190+
}
191+
}
192+
if (!lead) {
193+
f[i][mask] = ans;
194+
}
195+
return ans;
196+
};
197+
return dfs(dfs, n - 1, 0, true);
282198
}
283199
};
284200
```
@@ -287,39 +203,69 @@ public:
287203
288204
```go
289205
func countNumbersWithUniqueDigits(n int) int {
290-
dp := make([][]int, 10)
291-
for i := range dp {
292-
dp[i] = make([]int, 1<<11)
293-
for j := range dp[i] {
294-
dp[i][j] = -1
206+
f := make([][1 << 10]int, n)
207+
for i := range f {
208+
for j := range f[i] {
209+
f[i][j] = -1
295210
}
296211
}
297-
var dfs func(int, int, bool) int
298-
dfs = func(pos, mask int, lead bool) int {
299-
if pos <= 0 {
212+
var dfs func(i, mask int, lead bool) int
213+
dfs = func(i, mask int, lead bool) int {
214+
if i < 0 {
300215
return 1
301216
}
302-
if !lead && dp[pos][mask] != -1 {
303-
return dp[pos][mask]
217+
if !lead && f[i][mask] != -1 {
218+
return f[i][mask]
304219
}
305220
ans := 0
306-
for i := 0; i < 10; i++ {
307-
if ((mask >> i) & 1) == 1 {
221+
for j := 0; j < 10; j++ {
222+
if mask>>j&1 == 1 {
308223
continue
309224
}
310-
if i == 0 && lead {
311-
ans += dfs(pos-1, mask, lead)
225+
if lead && j == 0 {
226+
ans += dfs(i-1, mask, true)
312227
} else {
313-
ans += dfs(pos-1, mask|1<<i, false)
228+
ans += dfs(i-1, mask|1<<j, false)
314229
}
315230
}
316231
if !lead {
317-
dp[pos][mask] = ans
232+
f[i][mask] = ans
318233
}
319234
return ans
320235
}
236+
return dfs(n-1, 0, true)
237+
}
238+
```
239+
240+
#### TypeScript
321241

322-
return dfs(n, 0, true)
242+
```ts
243+
function countNumbersWithUniqueDigits(n: number): number {
244+
const f: number[][] = Array.from({ length: n }, () => Array(1 << 10).fill(-1));
245+
const dfs = (i: number, mask: number, lead: boolean): number => {
246+
if (i < 0) {
247+
return 1;
248+
}
249+
if (!lead && f[i][mask] !== -1) {
250+
return f[i][mask];
251+
}
252+
let ans = 0;
253+
for (let j = 0; j < 10; ++j) {
254+
if ((mask >> j) & 1) {
255+
continue;
256+
}
257+
if (lead && j === 0) {
258+
ans += dfs(i - 1, mask, true);
259+
} else {
260+
ans += dfs(i - 1, mask | (1 << j), false);
261+
}
262+
}
263+
if (!lead) {
264+
f[i][mask] = ans;
265+
}
266+
return ans;
267+
};
268+
return dfs(n - 1, 0, true);
323269
}
324270
```
325271

0 commit comments

Comments
 (0)