Skip to content

Commit 94c1156

Browse files
committed
feat: add solutions to lc problem: No.0233
No.0233.Number of Digit One
1 parent 9aa18cc commit 94c1156

File tree

7 files changed

+343
-164
lines changed

7 files changed

+343
-164
lines changed

solution/0200-0299/0233.Number of Digit One/README.md

Lines changed: 140 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,36 @@
3636

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

39-
经典数位 dp 问题,也可以用找规律解决
39+
**方法一:数位 DP**
40+
41+
这道题实际上是求在给定区间 $[l,..r]$ 中,数字中出现 $1$ 个数。个数与数的位数以及每一位上的数字有关。我们可以用数位 DP 的思路来解决这道题。数位 DP 中,数的大小对复杂度的影响很小。
42+
43+
对于区间 $[l,..r]$ 问题,我们一般会将其转化为 $[1,..r]$ 然后再减去 $[1,..l - 1]$ 的问题,即:
44+
45+
$$
46+
ans = \sum_{i=1}^{r} ans_i - \sum_{i=1}^{l-1} ans_i
47+
$$
48+
49+
不过对于本题而言,我们只需要求出区间 $[1,..r]$ 的值即可。
50+
51+
这里我们用记忆化搜索来实现数位 DP。从起点向下搜索,到最底层得到方案数,一层层向上返回答案并累加,最后从搜索起点得到最终的答案。
52+
53+
基本步骤如下:
54+
55+
1. 将数字 $n$ 转为 int 数组 $a$,其中 $a[1]$ 为最低位,而 $a[len]$ 为最高位;
56+
1. 根据题目信息,设计函数 $dfs()$,对于本题,我们定义 $dfs(pos, cnt, limit)$,答案为 $dfs(len, 0, true)$。
57+
58+
其中:
59+
60+
- `pos` 表示数字的位数,从末位或者第一位开始,一般根据题目的数字构造性质来选择顺序。对于本题,我们选择从高位开始,因此,`pos` 的初始值为 `len`
61+
- `cnt` 表示当前数字中包含的 $1$ 的个数。
62+
- `limit` 表示可填的数字的限制,如果无限制,那么可以选择 $[0,1,..9]$,否则,只能选择 $[0,..a[pos]]$。如果 `limit``true` 且已经取到了能取到的最大值,那么下一个 `limit` 同样为 `true`;如果 `limit``true` 但是还没有取到最大值,或者 `limit``false`,那么下一个 `limit``false`
63+
64+
关于函数的实现细节,可以参考下面的代码。
65+
66+
时间复杂度 $O(\log n)$。
67+
68+
相似题目:[788. 旋转数字](/solution/0700-0799/0788.Rotated%20Digits/README.md)
4069

4170
<!-- tabs:start -->
4271

@@ -47,27 +76,23 @@
4776
```python
4877
class Solution:
4978
def countDigitOne(self, n: int) -> int:
50-
dp = [[-1] * 10 for _ in range(10)]
51-
digit = []
52-
while n:
53-
digit.append(n % 10)
54-
n //= 10
55-
56-
def dfs(pos: int, cnt: int, limit: bool) -> int:
57-
if pos == -1:
79+
@cache
80+
def dfs(pos, cnt, limit):
81+
if pos <= 0:
5882
return cnt
59-
if not limit and dp[pos][cnt] != -1:
60-
return dp[pos][cnt]
61-
up = digit[pos] if limit else 9
83+
up = a[pos] if limit else 9
6284
ans = 0
6385
for i in range(up + 1):
64-
nxt = cnt + 1 if i == 1 else cnt
65-
ans += dfs(pos - 1, nxt, limit and i == digit[pos])
66-
if not limit:
67-
dp[pos][cnt] = ans
86+
ans += dfs(pos - 1, cnt + (i == 1), limit and i == up)
6887
return ans
6988

70-
return dfs(len(digit) - 1, 0, True)
89+
a = [0] * 12
90+
l = 1
91+
while n:
92+
a[l] = n % 10
93+
n //= 10
94+
l += 1
95+
return dfs(l, 0, True)
7196
```
7297

7398
### **Java**
@@ -76,90 +101,145 @@ class Solution:
76101

77102
```java
78103
class Solution {
104+
private int[] a = new int[12];
105+
private int[][] dp = new int[12][12];
106+
79107
public int countDigitOne(int n) {
80-
int index = 1;
81-
int count = 0;
82-
int high = n, cur = 0, low = 0;
83-
while (high > 0) {
84-
high /= 10;
85-
cur = (n / index) % 10;
86-
low = n - (n / index) * index;
87-
if (cur == 0) count += high * index;
88-
if (cur == 1) count += high * index + low + 1;
89-
if (cur > 1) count += (high + 1) * index;
90-
index *= 10;
108+
int len = 1;
109+
while (n > 0) {
110+
a[len++] = n % 10;
111+
n /= 10;
91112
}
92-
return count;
113+
for (var e : dp) {
114+
Arrays.fill(e, -1);
115+
}
116+
return dfs(len, 0, true);
117+
}
118+
119+
private int dfs(int pos, int cnt, boolean limit) {
120+
if (pos <= 0) {
121+
return cnt;
122+
}
123+
if (!limit && dp[pos][cnt] != -1) {
124+
return dp[pos][cnt];
125+
}
126+
int up = limit ? a[pos] : 9;
127+
int ans = 0;
128+
for (int i = 0; i <= up; ++i) {
129+
ans += dfs(pos - 1, cnt + (i == 1 ? 1 : 0), limit && i == up);
130+
}
131+
if (!limit) {
132+
dp[pos][cnt] = ans;
133+
}
134+
return ans;
93135
}
94136
}
95137
```
96138

97-
### **C#**
139+
### **C++**
98140

99-
```cs
100-
public class Solution {
101-
public int CountDigitOne(int n) {
102-
if (n <= 0) return 0;
103-
if (n < 10) return 1;
104-
return CountDigitOne(n / 10 - 1) * 10 + n / 10 + CountDigitOneOfN(n / 10) * (n % 10 + 1) + (n % 10 >= 1 ? 1 : 0);
141+
```cpp
142+
class Solution {
143+
public:
144+
int a[12];
145+
int dp[12][12];
146+
147+
int countDigitOne(int n) {
148+
int len = 1;
149+
while (n) {
150+
a[len++] = n % 10;
151+
n /= 10;
152+
}
153+
memset(dp, -1, sizeof dp);
154+
return dfs(len, 0, true);
105155
}
106156

107-
private int CountDigitOneOfN(int n) {
108-
var count = 0;
109-
while (n > 0)
110-
{
111-
if (n % 10 == 1) ++count;
112-
n /= 10;
157+
int dfs(int pos, int cnt, bool limit) {
158+
if (pos <= 0) {
159+
return cnt;
113160
}
114-
return count;
161+
if (!limit && dp[pos][cnt] != -1) {
162+
return dp[pos][cnt];
163+
}
164+
int ans = 0;
165+
int up = limit ? a[pos] : 9;
166+
for (int i = 0; i <= up; ++i) {
167+
ans += dfs(pos - 1, cnt + (i == 1), limit && i == up);
168+
}
169+
if (!limit) {
170+
dp[pos][cnt] = ans;
171+
}
172+
return ans;
115173
}
116-
}
174+
};
117175
```
118176
119177
### **Go**
120178
121179
```go
122180
func countDigitOne(n int) int {
123-
digit := make([]int, 0)
124-
for i := n; i > 0; i /= 10 {
125-
digit = append(digit, i%10)
126-
}
127-
128-
dp := make([][]int, 10)
181+
a := make([]int, 12)
182+
dp := make([][]int, 12)
129183
for i := range dp {
130-
dp[i] = make([]int, 10)
131-
for j := 0; j < 10; j++ {
184+
dp[i] = make([]int, 12)
185+
for j := range dp[i] {
132186
dp[i][j] = -1
133187
}
134188
}
135-
136-
var dfs func(pos, cnt int, limit bool) int
189+
l := 1
190+
for n > 0 {
191+
a[l] = n % 10
192+
n /= 10
193+
l++
194+
}
195+
var dfs func(int, int, bool) int
137196
dfs = func(pos, cnt int, limit bool) int {
138-
if pos == -1 {
197+
if pos <= 0 {
139198
return cnt
140199
}
141200
if !limit && dp[pos][cnt] != -1 {
142201
return dp[pos][cnt]
143202
}
144203
up := 9
145204
if limit {
146-
up = digit[pos]
205+
up = a[pos]
147206
}
148207
ans := 0
149208
for i := 0; i <= up; i++ {
150-
next := cnt
209+
t := cnt
151210
if i == 1 {
152-
next++
211+
t++
153212
}
154-
ans += dfs(pos-1, next, limit && i == digit[pos])
213+
ans += dfs(pos-1, t, limit && i == up)
155214
}
156215
if !limit {
157216
dp[pos][cnt] = ans
158217
}
159218
return ans
160219
}
220+
return dfs(l, 0, true)
221+
}
222+
```
223+
224+
### **C#**
225+
226+
```cs
227+
public class Solution {
228+
public int CountDigitOne(int n) {
229+
if (n <= 0) return 0;
230+
if (n < 10) return 1;
231+
return CountDigitOne(n / 10 - 1) * 10 + n / 10 + CountDigitOneOfN(n / 10) * (n % 10 + 1) + (n % 10 >= 1 ? 1 : 0);
232+
}
161233

162-
return dfs(len(digit)-1, 0, true)
234+
private int CountDigitOneOfN(int n) {
235+
var count = 0;
236+
while (n > 0)
237+
{
238+
if (n % 10 == 1) ++count;
239+
n /= 10;
240+
}
241+
return count;
242+
}
163243
}
164244
```
165245

0 commit comments

Comments
 (0)