90
90
91
91
基本步骤如下:
92
92
93
- 1 . 将数字 $n$ 转为 int 数组 $a$,其中 $a[ 1] $ 为最低位,而 $a[ len] $ 为最高位;
94
- 1 . 根据题目信息,设计函数 $dfs()$,对于本题,我们定义 $dfs(pos, lead, limit)$,答案为 $dfs(len, 1, true)$。
93
+ 我们将数字 $n$ 转化为字符串 $s$,记字符串 $s$ 的长度为 $m$。
95
94
96
- 其中:
95
+ 接下来,我们设计一个函数 $\textit{dfs}(i, \textit{lead}, \textit{limit})$,表示当前处理到字符串的第 $i$ 位,到最后一位的方案数。 其中:
97
96
98
- - ` pos ` 表示数字的位数,从末位或者第一位开始,一般根据题目的数字构造性质来选择顺序。对于本题,我们选择从高位开始,因此, ` pos ` 的初始值为 ` len ` ;
99
- - ` lead ` 表示当前数字中是否包含前导零,如果包含,则为 ` 1 ` ,否则为 ` 0 ` ;初始化为 ` 1 ` ;
100
- - ` limit ` 表示可填的数字的限制,如果无限制,那么可以选择 $ [ 0,1,..9 ] $,否则,只能选择 $ [ 0,..a [ pos ]] $。如果 ` limit ` 为 ` true ` 且已经取到了能取到的最大值,那么下一个 ` limit ` 同样为 ` true ` ;如果 ` limit ` 为 ` true ` 但是还没有取到最大值,或者 ` limit ` 为 ` false ` ,那么下一个 ` limit ` 为 ` false ` 。
97
+ - 数字 $i$ 表示当前处理到字符串 $s$ 的第 $i$ 位 ;
98
+ - 布尔值 $\textit{ lead}$ 表示是否只包含前导零 ;
99
+ - 布尔值 $\textit{limit}$ 表示当前位置是否受到上界的限制 。
101
100
102
- 关于函数的实现细节,可以参考下面的代码。
101
+ 函数的执行过程如下:
103
102
104
- 时间复杂度 $O(\log n)$。
103
+ 如果 $i$ 大于等于 $m$,说明我们已经处理完了所有的位数,此时如果 $\textit{lead}$ 为真,说明当前的数字是前导零,我们应当返回 $0$;否则,我们应当返回 $1$。
104
+
105
+ 否则,我们计算当前位置的上界 $\textit{up}$,如果 $\textit{limit}$ 为真,则 $up$ 为 $s[ i] $ 对应的数字,否则 $up$ 为 $9$。
106
+
107
+ 然后,我们在 $[ 0, \textit{up}] $ 的范围内枚举当前位置的数字 $j$,如果 $j$ 为 $0$ 且 $\textit{lead}$ 为真,我们递归计算 $\textit{dfs}(i + 1, \text{true}, \textit{limit} \wedge j = \textit{up})$;否则,如果 $j$ 在 $\textit{digits}$ 中,我们递归计算 $\textit{dfs}(i + 1, \text{false}, \textit{limit} \wedge j = \textit{up})$。累加所有的结果即为答案。
108
+
109
+ 最后,我们返回 $\textit{dfs}(0, \text{true}, \text{true})$ 即可。
110
+
111
+ 时间复杂度 $O(\log n \times D)$,空间复杂度 $O(\log n)$。其中 $D = 10$。
105
112
106
113
相似题目:
107
114
120
127
class Solution :
121
128
def atMostNGivenDigitSet (self , digits : List[str ], n : int ) -> int :
122
129
@cache
123
- def dfs (pos , lead , limit ):
124
- if pos <= 0 :
125
- return lead == False
126
- up = a[pos] if limit else 9
130
+ def dfs (i : int , lead : int , limit : bool ) -> int :
131
+ if i >= len (s):
132
+ return lead ^ 1
133
+
134
+ up = int (s[i]) if limit else 9
127
135
ans = 0
128
- for i in range (up + 1 ):
129
- if i == 0 and lead:
130
- ans += dfs(pos - 1 , lead , limit and i == up)
131
- elif i in s :
132
- ans += dfs(pos - 1 , False , limit and i == up)
136
+ for j in range (up + 1 ):
137
+ if j == 0 and lead:
138
+ ans += dfs(i + 1 , 1 , limit and j == up)
139
+ elif j in nums :
140
+ ans += dfs(i + 1 , 0 , limit and j == up)
133
141
return ans
134
142
135
- l = 0
136
- a = [0 ] * 12
137
- s = {int (d) for d in digits}
138
- while n:
139
- l += 1
140
- a[l] = n % 10
141
- n //= 10
142
- return dfs(l, True , True )
143
+ s = str (n)
144
+ nums = {int (x) for x in digits}
145
+ return dfs(0 , 1 , True )
143
146
```
144
147
145
148
#### Java
146
149
147
150
``` java
148
151
class Solution {
149
- private int [] a = new int [ 12 ] ;
150
- private int [][] dp = new int [ 12 ][ 2 ] ;
151
- private Set< Integer > s = new HashSet<> () ;
152
+ private Set< Integer > nums = new HashSet<> () ;
153
+ private char [] s ;
154
+ private Integer [] f ;
152
155
153
156
public int atMostNGivenDigitSet (String [] digits , int n ) {
154
- for (var e : dp) {
155
- Arrays . fill(e, - 1 );
157
+ s = String . valueOf(n). toCharArray();
158
+ f = new Integer [s. length];
159
+ for (var x : digits) {
160
+ nums. add(Integer . parseInt(x));
156
161
}
157
- for (String d : digits) {
158
- s. add(Integer . parseInt(d));
159
- }
160
- int len = 0 ;
161
- while (n > 0 ) {
162
- a[++ len] = n % 10 ;
163
- n /= 10 ;
164
- }
165
- return dfs(len, 1 , true );
162
+ return dfs(0 , true , true );
166
163
}
167
164
168
- private int dfs (int pos , int lead , boolean limit ) {
169
- if (pos <= 0 ) {
170
- return lead ^ 1 ;
165
+ private int dfs (int i , boolean lead , boolean limit ) {
166
+ if (i >= s . length ) {
167
+ return lead ? 0 : 1 ;
171
168
}
172
- if (! limit && lead != 1 && dp[pos][lead] != - 1 ) {
173
- return dp[pos][lead ];
169
+ if (! lead && ! limit && f[i] != null ) {
170
+ return f[i ];
174
171
}
172
+ int up = limit ? s[i] - ' 0' : 9 ;
175
173
int ans = 0 ;
176
- int up = limit ? a[pos] : 9 ;
177
- for (int i = 0 ; i <= up; ++ i) {
178
- if (i == 0 && lead == 1 ) {
179
- ans += dfs(pos - 1 , lead, limit && i == up);
180
- } else if (s. contains(i)) {
181
- ans += dfs(pos - 1 , 0 , limit && i == up);
174
+ for (int j = 0 ; j <= up; ++ j) {
175
+ if (j == 0 && lead) {
176
+ ans += dfs(i + 1 , true , limit && j == up);
177
+ } else if (nums. contains(j)) {
178
+ ans += dfs(i + 1 , false , limit && j == up);
182
179
}
183
180
}
184
- if (! limit && lead == 0 ) {
185
- dp[pos][lead ] = ans;
181
+ if (! lead && ! limit ) {
182
+ f[i ] = ans;
186
183
}
187
184
return ans;
188
185
}
@@ -194,43 +191,37 @@ class Solution {
194
191
``` cpp
195
192
class Solution {
196
193
public:
197
- int a[ 12] ;
198
- int dp[ 12] [ 2 ] ;
199
- unordered_set<int > s;
200
-
201
194
int atMostNGivenDigitSet(vector<string >& digits, int n) {
202
- memset(dp, -1, sizeof dp);
203
- for (auto& d : digits) {
204
- s.insert(stoi(d));
205
- }
206
- int len = 0 ;
207
- while (n) {
208
- a[++len] = n % 10;
209
- n /= 10;
195
+ string s = to_string(n);
196
+ unordered_set<int > nums;
197
+ for (auto& x : digits) {
198
+ nums.insert(stoi(x));
210
199
}
211
- return dfs(len, 1, true);
212
- }
213
-
214
- int dfs(int pos, int lead, bool limit) {
215
- if (pos <= 0) {
216
- return lead ^ 1;
217
- }
218
- if (!limit && !lead && dp[pos][lead] != -1) {
219
- return dp[pos][lead];
220
- }
221
- int ans = 0;
222
- int up = limit ? a[pos] : 9;
223
- for (int i = 0; i <= up; ++i) {
224
- if (i == 0 && lead) {
225
- ans += dfs(pos - 1, lead, limit && i == up);
226
- } else if (s.count(i)) {
227
- ans += dfs(pos - 1, 0, limit && i == up);
200
+ int m = s.size();
201
+ int f[ m] ;
202
+ memset(f, -1, sizeof(f));
203
+ auto dfs = [ &] (auto&& dfs, int i, bool lead, bool limit) -> int {
204
+ if (i >= m) {
205
+ return lead ? 0 : 1;
228
206
}
229
- }
230
- if (!limit && !lead) {
231
- dp[pos][lead] = ans;
232
- }
233
- return ans;
207
+ if (!lead && !limit && f[ i] != -1) {
208
+ return f[ i] ;
209
+ }
210
+ int up = limit ? s[ i] - '0' : 9;
211
+ int ans = 0;
212
+ for (int j = 0; j <= up; ++j) {
213
+ if (j == 0 && lead) {
214
+ ans += dfs(dfs, i + 1, true, limit && j == up);
215
+ } else if (nums.count(j)) {
216
+ ans += dfs(dfs, i + 1, false, limit && j == up);
217
+ }
218
+ }
219
+ if (!lead && !limit) {
220
+ f[ i] = ans;
221
+ }
222
+ return ans;
223
+ };
224
+ return dfs(dfs, 0, true, true);
234
225
}
235
226
};
236
227
```
@@ -239,48 +230,79 @@ public:
239
230
240
231
```go
241
232
func atMostNGivenDigitSet(digits []string, n int) int {
242
- s := map [int ]bool {}
243
- for _ , d := range digits {
244
- i , _ := strconv.Atoi (d)
245
- s[i] = true
233
+ s := strconv.Itoa(n)
234
+ m := len(s)
235
+ f := make([]int, m)
236
+ for i := range f {
237
+ f[i] = -1
246
238
}
247
- a := make ([]int , 12 )
248
- dp := make ([][2 ]int , 12 )
249
- for i := range a {
250
- dp[i] = [2 ]int {-1 , -1 }
251
- }
252
- l := 0
253
- for n > 0 {
254
- l++
255
- a[l] = n % 10
256
- n /= 10
239
+ nums := map[int]bool{}
240
+ for _, d := range digits {
241
+ x, _ := strconv.Atoi(d)
242
+ nums[x] = true
257
243
}
258
- var dfs func (int , int , bool ) int
259
- dfs = func (pos, lead int , limit bool ) int {
260
- if pos <= 0 {
261
- return lead ^ 1
244
+ var dfs func(i int, lead, limit bool) int
245
+ dfs = func(i int, lead, limit bool) int {
246
+ if i >= m {
247
+ if lead {
248
+ return 0
249
+ }
250
+ return 1
262
251
}
263
- if !limit && lead == 0 && dp[pos][lead ] != -1 {
264
- return dp[pos][lead ]
252
+ if !lead && !limit && f[i ] != -1 {
253
+ return f[i ]
265
254
}
266
255
up := 9
267
256
if limit {
268
- up = a[pos]
257
+ up = int(s[i] - '0')
269
258
}
270
259
ans := 0
271
- for i := 0 ; i <= up; i ++ {
272
- if i == 0 && lead == 1 {
273
- ans += dfs (pos- 1 , lead , limit && i == up)
274
- } else if s[i ] {
275
- ans += dfs (pos- 1 , 0 , limit && i == up)
260
+ for j := 0; j <= up; j ++ {
261
+ if j == 0 && lead {
262
+ ans += dfs(i+ 1, true , limit && j == up)
263
+ } else if nums[j ] {
264
+ ans += dfs(i+ 1, false , limit && j == up)
276
265
}
277
266
}
278
- if !limit {
279
- dp[pos][lead ] = ans
267
+ if !lead && ! limit {
268
+ f[i ] = ans
280
269
}
281
270
return ans
282
271
}
283
- return dfs (l, 1 , true )
272
+ return dfs(0, true, true)
273
+ }
274
+ ```
275
+
276
+ #### TypeScript
277
+
278
+ ``` ts
279
+ function atMostNGivenDigitSet(digits : string [], n : number ): number {
280
+ const s = n .toString ();
281
+ const m = s .length ;
282
+ const f: number [] = Array (m ).fill (- 1 );
283
+ const nums = new Set <number >(digits .map (d => parseInt (d )));
284
+ const dfs = (i : number , lead : boolean , limit : boolean ): number => {
285
+ if (i >= m ) {
286
+ return lead ? 0 : 1 ;
287
+ }
288
+ if (! lead && ! limit && f [i ] !== - 1 ) {
289
+ return f [i ];
290
+ }
291
+ const up = limit ? + s [i ] : 9 ;
292
+ let ans = 0 ;
293
+ for (let j = 0 ; j <= up ; ++ j ) {
294
+ if (! j && lead ) {
295
+ ans += dfs (i + 1 , true , limit && j === up );
296
+ } else if (nums .has (j )) {
297
+ ans += dfs (i + 1 , false , limit && j === up );
298
+ }
299
+ }
300
+ if (! lead && ! limit ) {
301
+ f [i ] = ans ;
302
+ }
303
+ return ans ;
304
+ };
305
+ return dfs (0 , true , true );
284
306
}
285
307
```
286
308
0 commit comments