Skip to content

Commit ce5147d

Browse files
authored
feat: add solutions to lc problem: No.0902 (#3544)
No.0902.Numbers At Most N Given Digit Set
1 parent da3ed3d commit ce5147d

File tree

7 files changed

+401
-347
lines changed

7 files changed

+401
-347
lines changed

Diff for: solution/0900-0999/0902.Numbers At Most N Given Digit Set/README.md

+137-115
Original file line numberDiff line numberDiff line change
@@ -90,18 +90,25 @@ $$
9090

9191
基本步骤如下:
9292

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$。
9594

96-
其中:
95+
接下来,我们设计一个函数 $\textit{dfs}(i, \textit{lead}, \textit{limit})$,表示当前处理到字符串的第 $i$ 位,到最后一位的方案数。其中:
9796

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}$ 表示当前位置是否受到上界的限制
101100

102-
关于函数的实现细节,可以参考下面的代码。
101+
函数的执行过程如下:
103102

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$。
105112

106113
相似题目:
107114

@@ -120,69 +127,59 @@ $$
120127
class Solution:
121128
def atMostNGivenDigitSet(self, digits: List[str], n: int) -> int:
122129
@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
127135
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)
133141
return ans
134142

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)
143146
```
144147

145148
#### Java
146149

147150
```java
148151
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;
152155

153156
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));
156161
}
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);
166163
}
167164

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;
171168
}
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];
174171
}
172+
int up = limit ? s[i] - '0' : 9;
175173
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);
182179
}
183180
}
184-
if (!limit && lead == 0) {
185-
dp[pos][lead] = ans;
181+
if (!lead && !limit) {
182+
f[i] = ans;
186183
}
187184
return ans;
188185
}
@@ -194,43 +191,37 @@ class Solution {
194191
```cpp
195192
class Solution {
196193
public:
197-
int a[12];
198-
int dp[12][2];
199-
unordered_set<int> s;
200-
201194
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));
210199
}
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;
228206
}
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);
234225
}
235226
};
236227
```
@@ -239,48 +230,79 @@ public:
239230
240231
```go
241232
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
246238
}
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
257243
}
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
262251
}
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]
265254
}
266255
up := 9
267256
if limit {
268-
up = a[pos]
257+
up = int(s[i] - '0')
269258
}
270259
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)
276265
}
277266
}
278-
if !limit {
279-
dp[pos][lead] = ans
267+
if !lead && !limit {
268+
f[i] = ans
280269
}
281270
return ans
282271
}
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);
284306
}
285307
```
286308

0 commit comments

Comments
 (0)