36
36
37
37
<!-- 这里可写通用的实现逻辑 -->
38
38
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 )
40
69
41
70
<!-- tabs:start -->
42
71
47
76
``` python
48
77
class Solution :
49
78
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 :
58
82
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
62
84
ans = 0
63
85
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)
68
87
return ans
69
88
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 )
71
96
```
72
97
73
98
### ** Java**
@@ -76,90 +101,145 @@ class Solution:
76
101
77
102
``` java
78
103
class Solution {
104
+ private int [] a = new int [12 ];
105
+ private int [][] dp = new int [12 ][12 ];
106
+
79
107
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 ;
91
112
}
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;
93
135
}
94
136
}
95
137
```
96
138
97
- ### ** C# **
139
+ ### ** C++ **
98
140
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);
105
155
}
106
156
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;
113
160
}
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;
115
173
}
116
- }
174
+ };
117
175
```
118
176
119
177
### **Go**
120
178
121
179
```go
122
180
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)
129
183
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] {
132
186
dp[i][j] = -1
133
187
}
134
188
}
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
137
196
dfs = func(pos, cnt int, limit bool) int {
138
- if pos == - 1 {
197
+ if pos <= 0 {
139
198
return cnt
140
199
}
141
200
if !limit && dp[pos][cnt] != -1 {
142
201
return dp[pos][cnt]
143
202
}
144
203
up := 9
145
204
if limit {
146
- up = digit [pos]
205
+ up = a [pos]
147
206
}
148
207
ans := 0
149
208
for i := 0; i <= up; i++ {
150
- next := cnt
209
+ t := cnt
151
210
if i == 1 {
152
- next ++
211
+ t ++
153
212
}
154
- ans += dfs (pos-1 , next , limit && i == digit[pos] )
213
+ ans += dfs(pos-1, t , limit && i == up )
155
214
}
156
215
if !limit {
157
216
dp[pos][cnt] = ans
158
217
}
159
218
return ans
160
219
}
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
+ }
161
233
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
+ }
163
243
}
164
244
```
165
245
0 commit comments