37
37
38
38
<!-- 这里可写通用的实现逻辑 -->
39
39
40
- 将 n 拆为两部分:最高位 high 和低位 lows。按 high 是否为 1 分别递归求解结果 f(n)。
40
+ ** 方法一:数位 DP **
41
41
42
- 以 n=3356 举例说明 。
42
+ 这道题实际上是求在给定区间 $ [ l,..r ] $ 中,数字中出现 $1$ 个数。个数与数的位数以及每一位上的数字有关。我们可以用数位 DP 的思路来解决这道题。数位 DP 中,数的大小对复杂度的影响很小 。
43
43
44
- high=3,lows=356,base=1000。此时 n 可拆分为 ` 0~999 ` , ` 1000~1999 ` , ` 2000~2999 ` , ` 3000~3356 ` ,其中 :
44
+ 对于区间 $ [ l,..r ] $ 问题,我们一般会将其转化为 $ [ 1,..r ] $ 然后再减去 $ [ 1,..l - 1 ] $ 的问题,即 :
45
45
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
+ $$
50
49
51
- 因此,1 的总个数为 ` high*f(base-1)+f(lows)+base ` 。
50
+ 不过对于本题而言,我们只需要求出区间 $ [ 1,..r ] $ 的值即可 。
52
51
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 )
54
77
55
78
<!-- tabs:start -->
56
79
@@ -60,19 +83,22 @@ high=3,lows=356,base=1000。此时 n 可拆分为 `0~999`,`1000~1999`,`2000~2999
60
83
61
84
``` python
62
85
class Solution :
63
- @cache
64
86
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 )
76
102
```
77
103
78
104
### ** Java**
@@ -81,17 +107,104 @@ class Solution:
81
107
82
108
``` java
83
109
class Solution {
110
+ private int [] a = new int [12 ];
111
+ private Integer [][] f = new Integer [12 ][12 ];
112
+
84
113
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;
87
150
}
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);
94
166
}
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)
95
208
}
96
209
```
97
210
@@ -125,35 +238,6 @@ var countDigitOne = function (n) {
125
238
};
126
239
```
127
240
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
-
157
241
### ** C#**
158
242
159
243
``` cs
0 commit comments