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 38abece

Browse files
committed
feat: add solutions to lcof problem: No.43
1 parent 4d07a0f commit 38abece

File tree

5 files changed

+240
-98
lines changed

5 files changed

+240
-98
lines changed

‎lcof/面试题43. 1〜n整数中1出现的次数/README.md‎

Lines changed: 142 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,43 @@
3737

3838
<!-- 这里可写通用的实现逻辑 -->
3939

40-
将 n 拆为两部分:最高位 high 和低位 lows。按 high 是否为 1 分别递归求解结果 f(n)。
40+
**方法一:数位 DP**
4141

42-
以 n=3356 举例说明
42+
这道题实际上是求在给定区间 $[l,..r]$ 中,数字中出现 1ドル$ 个数。个数与数的位数以及每一位上的数字有关。我们可以用数位 DP 的思路来解决这道题。数位 DP 中,数的大小对复杂度的影响很小
4343

44-
high=3,lows=356,base=1000。此时 n 可拆分为 `0~999`,`1000~1999`,`2000~2999`,`3000~3356`,其中:
44+
对于区间 $[l,..r]$ 问题,我们一般会将其转化为 $[1,..r]$ 然后再减去 $[1,..l - 1]$ 的问题,即:
4545

46-
- 0~999 范围内 1 的个数为 f(base-1)
47-
- 1000~1999 范围内 1 的个数可分为两部分:千位、其余位。千位都为 1,所以 1 的个数为 base+f(base-1)
48-
- 2000~2999 范围内 1 的个数为 f(base-1)
49-
- 3000~3356 范围内 1 的个数为 f(lows)
46+
$$
47+
ans = \sum_{i=1}^{r} ans_i - \sum_{i=1}^{l-1} ans_i
48+
$$
5049

51-
因此,1 的总个数为 `high*f(base-1)+f(lows)+base`
50+
不过对于本题而言,我们只需要求出区间 $[1,..r]$ 的值即可
5251

53-
最高位非 1 的情况,也可以按照同样的方法分析。
52+
这里我们用记忆化搜索来实现数位 DP。从起点向下搜索,到最底层得到方案数,一层层向上返回答案并累加,最后从搜索起点得到最终的答案。
53+
54+
基本步骤如下:
55+
56+
1. 将数字 $n$ 转为 int 数组 $a,ドル其中 $a[0]$ 为最低位,而 $a[i]$ 为最高位;
57+
1. 根据题目信息,设计函数 $dfs(),ドル对于本题,我们定义 $dfs(pos, cnt, limit),ドル其中:
58+
59+
- `pos` 表示数字的位数,从末位或者第一位开始,一般根据题目的数字构造性质来选择顺序。对于本题,我们选择从高位开始,因此,`pos` 的初始值为 `len`;
60+
- `cnt` 表示当前数字中包含的 1ドル$ 的个数。
61+
- `limit` 表示可填的数字的限制,如果无限制,那么可以选择 $[0,1,..9],ドル否则,只能选择 $[0,..a[pos]]$。如果 `limit``true` 且已经取到了能取到的最大值,那么下一个 `limit` 同样为 `true`;如果 `limit``true` 但是还没有取到最大值,或者 `limit``false`,那么下一个 `limit``false`
62+
63+
那么答案为 $dfs(i, 0, true)$。
64+
65+
关于函数的实现细节,可以参考下面的代码。
66+
67+
时间复杂度 $O(\log n)$。
68+
69+
相似题目:
70+
71+
- [357. 统计各位数字都不同的数字个数](/solution/0300-0399/0357.Count%20Numbers%20with%20Unique%20Digits/README.md)
72+
- [600. 不含连续 1 的非负整数](/solution/0600-0699/0600.Non-negative%20Integers%20without%20Consecutive%20Ones/README.md)
73+
- [788. 旋转数字](/solution/0700-0799/0788.Rotated%20Digits/README.md)
74+
- [902. 最大为 N 的数字组合](/solution/0900-0999/0902.Numbers%20At%20Most%20N%20Given%20Digit%20Set/README.md)
75+
- [1012. 至少有 1 位重复的数字](/solution/1000-1099/1012.Numbers%20With%20Repeated%20Digits/README.md)
76+
- [2376. 统计特殊整数](/solution/2300-2399/2376.Count%20Special%20Integers/README.md)
5477

5578
<!-- tabs:start -->
5679

@@ -60,19 +83,22 @@ high=3,lows=356,base=1000。此时 n 可拆分为 `0~999`,`1000~1999`,`2000~2999
6083

6184
```python
6285
class Solution:
63-
@cache
6486
def countDigitOne(self, n: int) -> int:
65-
if n < 1:
66-
return 0
67-
s = str(n)
68-
high = int(s[0])
69-
base = pow(10, len(s) - 1)
70-
lows = n % base
71-
return (
72-
self.countDigitOne(base - 1) + lows + 1 + self.countDigitOne(lows)
73-
if high == 1
74-
else high * self.countDigitOne(base - 1) + base + self.countDigitOne(lows)
75-
)
87+
@cache
88+
def dfs(pos, cnt, limit):
89+
if pos < 0:
90+
return cnt
91+
up = a[pos] if limit else 9
92+
ans = 0
93+
for i in range(up + 1):
94+
ans += dfs(pos - 1, cnt + (i == 1), limit and i == up)
95+
return ans
96+
97+
a = []
98+
while n:
99+
a.append(n % 10)
100+
n //= 10
101+
return dfs(len(a) - 1, 0, True)
76102
```
77103

78104
### **Java**
@@ -81,17 +107,104 @@ class Solution:
81107

82108
```java
83109
class Solution {
110+
private int[] a = new int[12];
111+
private Integer[][] f = new Integer[12][12];
112+
84113
public int countDigitOne(int n) {
85-
if (n < 1) {
86-
return 0;
114+
int i = -1;
115+
for (; n > 0; n /= 10) {
116+
a[++i] = n % 10;
117+
}
118+
return dfs(i, 0, true);
119+
}
120+
121+
private int dfs(int pos, int cnt, boolean limit) {
122+
if (pos < 0) {
123+
return cnt;
124+
}
125+
if (!limit && f[pos][cnt] != null) {
126+
return f[pos][cnt];
127+
}
128+
int up = limit ? a[pos] : 9;
129+
int ans = 0;
130+
for (int i = 0; i <= up; ++i) {
131+
ans += dfs(pos - 1, cnt + (i == 1 ? 1 : 0), limit && i == up);
132+
}
133+
return f[pos][cnt] = ans;
134+
}
135+
}
136+
```
137+
138+
### **C++**
139+
140+
```cpp
141+
class Solution {
142+
public:
143+
int countDigitOne(int n) {
144+
int a[12]{};
145+
int f[12][12];
146+
memset(f, -1, sizeof f);
147+
int i = -1;
148+
for (; n; n /= 10) {
149+
a[++i] = n % 10;
87150
}
88-
String s = String.valueOf(n);
89-
int high = s.charAt(0) - '0'; // 最高位
90-
int base = (int) Math.pow(10, s.length() - 1); // 基数
91-
int lows = n % base; // 低位
92-
return high == 1 ? countDigitOne(base - 1) + countDigitOne(lows) + lows + 1
93-
: high * countDigitOne(base - 1) + countDigitOne(lows) + base;
151+
function<int(int, int, bool)> dfs = [&](int pos, int cnt, bool limit) -> int {
152+
if (pos < 0) {
153+
return cnt;
154+
}
155+
if (!limit && f[pos][cnt] != -1) {
156+
return f[pos][cnt];
157+
}
158+
int up = limit ? a[pos] : 9;
159+
int ans = 0;
160+
for (int i = 0; i <= up; ++i) {
161+
ans += dfs(pos - 1, cnt + (i == 1), limit && i == up);
162+
}
163+
return f[pos][cnt] = ans;
164+
};
165+
return dfs(i, 0, true);
94166
}
167+
};
168+
```
169+
170+
```go
171+
func countDigitOne(n int) int {
172+
a := [12]int{}
173+
f := [12][12]int{}
174+
for i := range f {
175+
for j := range f[i] {
176+
f[i][j] = -1
177+
}
178+
}
179+
i := -1
180+
for ; n > 0; n /= 10 {
181+
i++
182+
a[i] = n % 10
183+
}
184+
var dfs func(int, int, bool) int
185+
dfs = func(pos, cnt int, limit bool) int {
186+
if pos < 0 {
187+
return cnt
188+
}
189+
if !limit && f[pos][cnt] != -1 {
190+
return f[pos][cnt]
191+
}
192+
up := 9
193+
if limit {
194+
up = a[pos]
195+
}
196+
ans := 0
197+
for i := 0; i <= up; i++ {
198+
t := 0
199+
if i == 1 {
200+
t++
201+
}
202+
ans += dfs(pos-1, cnt+t, limit && i == up)
203+
}
204+
f[pos][cnt] = ans
205+
return ans
206+
}
207+
return dfs(i, 0, true)
95208
}
96209
```
97210

@@ -125,35 +238,6 @@ var countDigitOne = function (n) {
125238
};
126239
```
127240

128-
### **C++**
129-
130-
```cpp
131-
class Solution {
132-
public:
133-
int countDigitOne(int n) {
134-
long long digit = 1;
135-
int count = 0;
136-
int high = n / 10;
137-
int cur = n % 10;
138-
int low = 0;
139-
while (high != 0 || cur != 0) {
140-
if (cur == 0) {
141-
count += high * digit;
142-
} else if (cur == 1) {
143-
count += high * digit + low + 1;
144-
} else {
145-
count += (high + 1) * digit;
146-
}
147-
low += cur * digit;
148-
cur = high % 10;
149-
high /= 10;
150-
digit *= 10;
151-
}
152-
return count;
153-
}
154-
};
155-
```
156-
157241
### **C#**
158242

159243
```cs
Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
class Solution {
22
public:
33
int countDigitOne(int n) {
4-
long long digit = 1;
5-
int count = 0;
6-
int high = n / 10;
7-
int cur = n % 10;
8-
int low = 0;
9-
while (high != 0 || cur != 0) {
10-
if (cur == 0) {
11-
count += high * digit;
12-
} else if (cur == 1) {
13-
count += high * digit + low + 1;
14-
} else {
15-
count += (high + 1) * digit;
16-
}
17-
low += cur * digit;
18-
cur = high % 10;
19-
high /= 10;
20-
digit *= 10;
4+
int a[12]{};
5+
int f[12][12];
6+
memset(f, -1, sizeof f);
7+
int i = -1;
8+
for (; n; n /= 10) {
9+
a[++i] = n % 10;
2110
}
22-
return count;
11+
function<int(int, int, bool)> dfs = [&](int pos, int cnt, bool limit) -> int {
12+
if (pos < 0) {
13+
return cnt;
14+
}
15+
if (!limit && f[pos][cnt] != -1) {
16+
return f[pos][cnt];
17+
}
18+
int up = limit ? a[pos] : 9;
19+
int ans = 0;
20+
for (int i = 0; i <= up; ++i) {
21+
ans += dfs(pos - 1, cnt + (i == 1), limit && i == up);
22+
}
23+
return f[pos][cnt] = ans;
24+
};
25+
return dfs(i, 0, true);
2326
}
24-
};
27+
};
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
func countDigitOne(n int) int {
2+
a := [12]int{}
3+
f := [12][12]int{}
4+
for i := range f {
5+
for j := range f[i] {
6+
f[i][j] = -1
7+
}
8+
}
9+
i := -1
10+
for ; n > 0; n /= 10 {
11+
i++
12+
a[i] = n % 10
13+
}
14+
var dfs func(int, int, bool) int
15+
dfs = func(pos, cnt int, limit bool) int {
16+
if pos < 0 {
17+
return cnt
18+
}
19+
if !limit && f[pos][cnt] != -1 {
20+
return f[pos][cnt]
21+
}
22+
up := 9
23+
if limit {
24+
up = a[pos]
25+
}
26+
ans := 0
27+
for i := 0; i <= up; i++ {
28+
t := 0
29+
if i == 1 {
30+
t++
31+
}
32+
ans += dfs(pos-1, cnt+t, limit && i == up)
33+
}
34+
f[pos][cnt] = ans
35+
return ans
36+
}
37+
return dfs(i, 0, true)
38+
}
Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
11
class Solution {
2+
private int[] a = new int[12];
3+
private Integer[][] f = new Integer[12][12];
4+
25
public int countDigitOne(int n) {
3-
if (n < 1) {
4-
return 0;
5-
}
6-
String s = String.valueOf(n);
7-
int high = s.charAt(0) - '0'; // 最高位
8-
int base = (int) Math.pow(10, s.length() - 1); // 基数
9-
int lows = n % base; // 低位
10-
return high == 1 ? countDigitOne(base - 1) + countDigitOne(lows) + lows + 1
11-
: high * countDigitOne(base - 1) + countDigitOne(lows) + base;
6+
int i = -1;
7+
for (; n > 0; n /= 10) {
8+
a[++i] = n % 10;
9+
}
10+
return dfs(i, 0, true);
11+
}
12+
13+
private int dfs(int pos, int cnt, boolean limit) {
14+
if (pos < 0) {
15+
return cnt;
16+
}
17+
if (!limit && f[pos][cnt] != null) {
18+
return f[pos][cnt];
19+
}
20+
int up = limit ? a[pos] : 9;
21+
int ans = 0;
22+
for (int i = 0; i <= up; ++i) {
23+
ans += dfs(pos - 1, cnt + (i == 1 ? 1 : 0), limit && i == up);
24+
}
25+
return f[pos][cnt] = ans;
1226
}
1327
}
Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
class Solution:
2-
@cache
32
def countDigitOne(self, n: int) -> int:
4-
if n < 1:
5-
return 0
6-
s = str(n)
7-
high = int(s[0])
8-
base = pow(10, len(s) - 1)
9-
lows = n % base
10-
return (
11-
self.countDigitOne(base - 1) + lows + 1 + self.countDigitOne(lows)
12-
if high == 1
13-
else high * self.countDigitOne(base - 1) + base + self.countDigitOne(lows)
14-
)
3+
@cache
4+
def dfs(pos, cnt, limit):
5+
if pos < 0:
6+
return cnt
7+
up = a[pos] if limit else 9
8+
ans = 0
9+
for i in range(up + 1):
10+
ans += dfs(pos - 1, cnt + (i == 1), limit and i == up)
11+
return ans
12+
13+
a = []
14+
while n:
15+
a.append(n % 10)
16+
n //= 10
17+
return dfs(len(a) - 1, 0, True)

0 commit comments

Comments
(0)

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