45
45
46
46
<!-- 这里可写通用的实现逻辑 -->
47
47
48
- ** 方法一:数位 DP**
48
+ ** 方法一:状态压缩 + 数位 DP**
49
49
50
- 题目求解 $[ 1,n] $ 范围内至少有 $1$ 位重复数字的正整数个数,我们可以转换为求解无重复数字的正整数个数 $cnt$,那么 $n-cnt$ 就是答案 。
50
+ 题目要求统计 $[ 1,.. n] $ 中至少有一位重复的数字的个数,我们可以换一种思路,用一个函数 $f(n)$ 统计 $ [ 1,..n ] $ 中没有重复数字的个数,那么答案就是 $n - f(n)$ 。
51
51
52
- 接下来我们就来求解 $ [ 1,n ] $ 范围内无重复数字的正整数个数 。
52
+ 另外,我们可以用一个二进制数来记录数字中出现过的数字,比如数字中出现了 $1$, $2$, $4$,那么对应的二进制数就是 $\underline{1}0\underline{1}\underline{1}0$ 。
53
53
54
- 定义 $m$ 表示数字 $n$ 的位数。我们可以将数字分成两类:(1) 数字位数小于 $m$;(2) 数字位数等于 $m$ 。
54
+ 接下来,我们用记忆化搜索来实现数位 DP。从起点向下搜索,到最底层得到方案数,一层层向上返回答案并累加,最后从搜索起点得到最终的答案 。
55
55
56
- 对于第一类,我们可以枚举数字的位数 $i$,其中 $i∈ [ 1,m)$,第一位的数字不为 $0$,有 $ [ 1,9 ] $ 可选,共 $9$ 种可能。剩余需要选择 $i-1$ 位数字,可选数字为 $ [ 0,9 ] $ 的数字中除去第一位,共 $9$ 种可能。因此,第一类的数字共有 :
56
+ 基本步骤如下 :
57
57
58
- $$
59
- \sum \limits_{i=1}^{m-1} 9\times A_{9}^{i-1}
60
- $$
58
+ 1 . 将数字 $n$ 转为整型数组 $nums$,其中 $nums[ 0] $ 为最低位,而 $nums[ i] $ 为最高位;
59
+ 1 . 根据题目信息,设计函数 $dfs()$,对于本题,我们定义 $dfs(pos, mask, lead, limit)$,其中:
61
60
62
- 对于第二类,数字的位数等于 $m$,我们从 $n$ 的高位(即 $i=m-1$)开始处理。不妨设 $n$ 当前位的数字为 $v$。
61
+ - 参数 $pos$ 表示当前搜索到的数字的位数,从末位或者第一位开始,一般根据题目的数字构造性质来选择顺序。对于本题,我们选择从高位开始,因此,$pos$ 的初始值为数字的高位下标;
62
+ - 参数 $mask$ 表示当前数字中出现过的数字;
63
+ - 参数 $lead$ 表示当前数字是否仅包含前导零;
64
+ - 参数 $limit$ 表示当前可填的数字的限制,如果无限制,那么可以选择 $i \in [ 0,1,..9] $,否则,只能选择 $i \in [ 0,..nums[ pos]] $。如果 $limit$ 为 ` true ` 且已经取到了能取到的最大值,那么下一个 $limit$ 同样为 ` true ` ;如果 $limit$ 为 ` true ` 但是还没有取到最大值,或者 $limit$ 为 ` false ` ,那么下一个 $limit$ 为 ` false ` 。
63
65
64
- 如果当前是 $n$ 的最高一位,那么数字不能为 $0$,可选数字为 $ [ 1,v)$,否则可选数字为 $ [ 0,v)$。若当前可选数字 $j$,那么剩余低位可选的数字总共有 $A _ {10-(m-i)}^{i}$,累加到答案中 。
66
+ 答案为 $dfs(0, 0, true, true)$ 。
65
67
66
- 以上我们算的是可选数字小于 $v$ 的情况,若等于 $v$,则需要继续外层循环,继续处理下一位。如果数字 $n$ 所有位均不重复,则 $n$ 本身也是一个特殊整数,需要累加到答案中 。
68
+ 关于函数的实现细节,可以参考下面的代码 。
67
69
68
- 时间复杂度 $O(m^2 )$,其中 $m$ 是数字 $n$ 的位数,这里我们假定 $A _ {m}^{n}$ 可以 $O(1)$ 时间算出 。
70
+ 时间复杂度 $O(m \times 2^m \times 10 )$,空间复杂度 $O(m \times 2^m)$。其中 $m$ 为数字 $n$ 的位数 。
69
71
70
72
相似题目:
71
73
@@ -117,29 +119,27 @@ class Solution:
117
119
def numDupDigitsAtMostN (self , n : int ) -> int :
118
120
return n - self .f(n)
119
121
120
- def f (self , n ) :
122
+ def f (self , n : int ) -> int :
121
123
@cache
122
- def dfs (pos , mask , lead , limit ) :
123
- if pos <= 0 :
124
- return lead ^ 1
125
- up = a [pos] if limit else 9
124
+ def dfs (pos : int , mask : int , lead : bool , limit : bool ) -> int :
125
+ if pos < 0 :
126
+ return int ( lead) ^ 1
127
+ up = nums [pos] if limit else 9
126
128
ans = 0
127
129
for i in range (up + 1 ):
128
- if ( mask >> i) & 1 :
130
+ if mask >> i & 1 :
129
131
continue
130
132
if i == 0 and lead:
131
133
ans += dfs(pos - 1 , mask, lead, limit and i == up)
132
134
else :
133
135
ans += dfs(pos - 1 , mask | 1 << i, False , limit and i == up)
134
136
return ans
135
137
136
- a = [0 ] * 11
137
- l = 0
138
+ nums = []
138
139
while n:
139
- l += 1
140
- a[l] = n % 10
140
+ nums.append(n % 10 )
141
141
n //= 10
142
- return dfs(l , 0 , True , True )
142
+ return dfs(len (nums) - 1 , 0 , True , True )
143
143
```
144
144
145
145
### ** Java**
@@ -191,36 +191,32 @@ class Solution {
191
191
192
192
``` java
193
193
class Solution {
194
- private int [] a = new int [11 ];
195
- private int [][] dp = new int [11 ][1 << 11 ];
194
+ private int [] nums = new int [11 ];
195
+ private Integer [][] dp = new Integer [11 ][1 << 11 ];
196
196
197
197
public int numDupDigitsAtMostN (int n ) {
198
198
return n - f(n);
199
199
}
200
200
201
201
private int f (int n ) {
202
- for (var e : dp) {
203
- Arrays . fill(e, - 1 );
204
- }
205
- int len = 0 ;
206
- while (n > 0 ) {
207
- a[++ len] = n % 10 ;
208
- n /= 10 ;
202
+ int i = - 1 ;
203
+ for (; n > 0 ; n /= 10 ) {
204
+ nums[++ i] = n % 10 ;
209
205
}
210
- return dfs(len , 0 , true , true );
206
+ return dfs(i , 0 , true , true );
211
207
}
212
208
213
209
private int dfs (int pos , int mask , boolean lead , boolean limit ) {
214
- if (pos <= 0 ) {
210
+ if (pos < 0 ) {
215
211
return lead ? 0 : 1 ;
216
212
}
217
- if (! lead && ! limit && dp[pos][mask] != - 1 ) {
213
+ if (! lead && ! limit && dp[pos][mask] != null ) {
218
214
return dp[pos][mask];
219
215
}
220
- int up = limit ? a[pos] : 9 ;
221
216
int ans = 0 ;
217
+ int up = limit ? nums[pos] : 9 ;
222
218
for (int i = 0 ; i <= up; ++ i) {
223
- if ((( mask >> i) & 1 ) == 1 ) {
219
+ if ((mask >> i & 1 ) == 1 ) {
224
220
continue ;
225
221
}
226
222
if (i == 0 && lead) {
@@ -285,34 +281,36 @@ public:
285
281
```cpp
286
282
class Solution {
287
283
public:
288
- int a[11];
289
- int dp[11][1 << 11];
290
-
291
284
int numDupDigitsAtMostN(int n) {
292
285
return n - f(n);
293
286
}
294
287
288
+ private:
289
+ int nums[11];
290
+ int dp[11][1 << 11];
291
+
295
292
int f(int n) {
296
- memset(dp, -1, sizeof dp);
297
- int len = 0;
298
- while (n) {
299
- a[++len] = n % 10;
300
- n /= 10;
293
+ memset(dp, -1, sizeof(dp));
294
+ int i = -1;
295
+ for (; n; n /= 10) {
296
+ nums[++i] = n % 10;
301
297
}
302
- return dfs(len , 0, true, true);
298
+ return dfs(i , 0, true, true);
303
299
}
304
300
305
301
int dfs(int pos, int mask, bool lead, bool limit) {
306
- if (pos <= 0) {
302
+ if (pos < 0) {
307
303
return lead ? 0 : 1;
308
304
}
309
305
if (!lead && !limit && dp[pos][mask] != -1) {
310
306
return dp[pos][mask];
311
307
}
312
- int up = limit ? a [pos] : 9;
308
+ int up = limit ? nums [pos] : 9;
313
309
int ans = 0;
314
310
for (int i = 0; i <= up; ++i) {
315
- if ((mask >> i) & 1) continue;
311
+ if (mask >> i & 1) {
312
+ continue;
313
+ }
316
314
if (i == 0 && lead) {
317
315
ans += dfs(pos - 1, mask, lead, limit && i == up);
318
316
} else {
@@ -382,23 +380,19 @@ func numDupDigitsAtMostN(n int) int {
382
380
}
383
381
384
382
func f (n int ) int {
385
- a := make ([]int , 11 )
386
- dp := make ([][]int , 11 )
383
+ nums := []int {}
384
+ for ; n > 0 ; n /= 10 {
385
+ nums = append (nums, n%10 )
386
+ }
387
+ dp := [11 ][1 << 11 ]int {}
387
388
for i := range dp {
388
- dp[i] = make ([]int , 1 <<11 )
389
389
for j := range dp[i] {
390
390
dp[i][j] = -1
391
391
}
392
392
}
393
- l := 0
394
- for n > 0 {
395
- l++
396
- a[l] = n % 10
397
- n /= 10
398
- }
399
393
var dfs func (int , int , bool , bool ) int
400
- dfs = func (pos, mask int , lead, limit bool ) int {
401
- if pos <= 0 {
394
+ dfs = func (pos int , mask int , lead bool , limit bool ) int {
395
+ if pos < 0 {
402
396
if lead {
403
397
return 0
404
398
}
@@ -407,13 +401,13 @@ func f(n int) int {
407
401
if !lead && !limit && dp[pos][mask] != -1 {
408
402
return dp[pos][mask]
409
403
}
410
- ans := 0
411
404
up := 9
412
405
if limit {
413
- up = a [pos]
406
+ up = nums [pos]
414
407
}
408
+ ans := 0
415
409
for i := 0 ; i <= up; i++ {
416
- if (( mask >> i) & 1 ) == 1 {
410
+ if mask>>i& 1 == 1 {
417
411
continue
418
412
}
419
413
if i == 0 && lead {
@@ -427,8 +421,54 @@ func f(n int) int {
427
421
}
428
422
return ans
429
423
}
424
+ return dfs (len (nums)-1 , 0 , true , true )
425
+ }
426
+ ```
427
+
428
+ ### ** TypeScript**
430
429
431
- return dfs (l, 0 , true , true )
430
+ ``` ts
431
+ function numDupDigitsAtMostN(n : number ): number {
432
+ return n - f (n );
433
+ }
434
+
435
+ function f(n : number ): number {
436
+ const nums: number [] = [];
437
+ let i = - 1 ;
438
+ for (; n ; n = Math .floor (n / 10 )) {
439
+ nums [++ i ] = n % 10 ;
440
+ }
441
+ const dp = Array .from ({ length: 11 }, () => Array (1 << 11 ).fill (- 1 ));
442
+ const dfs = (
443
+ pos : number ,
444
+ mask : number ,
445
+ lead : boolean ,
446
+ limit : boolean ,
447
+ ): number => {
448
+ if (pos < 0 ) {
449
+ return lead ? 0 : 1 ;
450
+ }
451
+ if (! lead && ! limit && dp [pos ][mask ] !== - 1 ) {
452
+ return dp [pos ][mask ];
453
+ }
454
+ const up = limit ? nums [pos ] : 9 ;
455
+ let ans = 0 ;
456
+ for (let i = 0 ; i <= up ; ++ i ) {
457
+ if ((mask >> i ) & 1 ) {
458
+ continue ;
459
+ }
460
+ if (lead && i === 0 ) {
461
+ ans += dfs (pos - 1 , mask , lead , limit && i === up );
462
+ } else {
463
+ ans += dfs (pos - 1 , mask | (1 << i ), false , limit && i === up );
464
+ }
465
+ }
466
+ if (! lead && ! limit ) {
467
+ dp [pos ][mask ] = ans ;
468
+ }
469
+ return ans ;
470
+ };
471
+ return dfs (i , 0 , true , true );
432
472
}
433
473
```
434
474
0 commit comments