Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 9492cca

Browse files
feat: add solutions to lc problem: No.0357 (doocs#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‎

Lines changed: 113 additions & 167 deletions
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 == 0and 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 =newint[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)

AltStyle によって変換されたページ (->オリジナル) /