6666
6767基本步骤如下:
6868
69- 1 . 将数字 $n$ 转为 int 数组 $a$,其中 $a[ 1] $ 为最低位,而 $a[ len] $ 为最高位;
70- 1 . 根据题目信息,设计函数 $dfs()$,对于本题,我们定义 $dfs(pos, cnt, limit)$,答案为 $dfs(len, 0, true)$。
69+ 我们首先将数字 $n$ 转化为字符串 $s$。然后我们设计一个函数 $\textit{dfs}(i, \textit{cnt}, \textit{limit})$,其中:
7170
72- 其中:
71+ - 数字 $i$ 表示当前搜索到的位置,我们从高位开始搜索,即 $i = 0$ 表示最高位。
72+ - 数字 $\textit{cnt}$ 表示当前数字中 $1$ 出现的次数。
73+ - 布尔值 $\textit{limit}$ 表示当前是否受到上界的限制。
7374
74- - ` pos ` 表示数字的位数,从末位或者第一位开始,一般根据题目的数字构造性质来选择顺序。对于本题,我们选择从高位开始,因此,` pos ` 的初始值为 ` len ` ;
75- - ` cnt ` 表示当前数字中包含的 $1$ 的个数。
76- - ` limit ` 表示可填的数字的限制,如果无限制,那么可以选择 $[ 0,1,..9] $,否则,只能选择 $[ 0,..a[ pos]] $。如果 ` limit ` 为 ` true ` 且已经取到了能取到的最大值,那么下一个 ` limit ` 同样为 ` true ` ;如果 ` limit ` 为 ` true ` 但是还没有取到最大值,或者 ` limit ` 为 ` false ` ,那么下一个 ` limit ` 为 ` false ` 。
75+ 函数的执行过程如下:
7776
78- 关于函数的实现细节,可以参考下面的代码。
77+ 如果 $i$ 超过了数字 $n$ 的长度,说明搜索结束,直接返回 $cnt$。如果 $\textit{limit}$ 为真,那么 $up$ 为当前数字的第 $i$ 位,否则 $up = 9$。接下来,我们遍历 $j$ 从 $0$ 到 $up$,对于每一个 $j$:
7978
80- 时间复杂度 $O(\log n)$。
79+ - 如果 $j$ 等于 $1$,我们将 $cnt$ 加一。
80+ - 递归调用 $\textit{dfs}(i + 1, \textit{cnt}, \textit{limit} \land j = up)$。
81+
82+ 答案为 $\textit{dfs}(0, 0, \text{True})$。
83+
84+ 时间复杂度 $O(m^2 \times D)$,空间复杂度 $O(m^2)$。其中 $m$ 为数字 $n$ 的长度,而 $D = 10$。
8185
8286相似题目:
8387
96100class Solution :
97101 def countDigitOne (self , n : int ) -> int :
98102 @cache
99- def dfs (pos , cnt , limit ) :
100- if pos <= 0 :
103+ def dfs (i : int , cnt : int , limit : bool ) -> int :
104+ if i >= len (s) :
101105 return cnt
102- up = a[pos] if limit else 9
106+ up = int (s[i]) if limit else 9
103107 ans = 0
104- for i in range (up + 1 ):
105- ans += dfs(pos - 1 , cnt + (i == 1 ), limit and i == up)
108+ for j in range (up + 1 ):
109+ ans += dfs(i + 1 , cnt + (j == 1 ), limit and j == up)
106110 return ans
107111
108- a = [0 ] * 12
109- l = 1
110- while n:
111- a[l] = n % 10
112- n //= 10
113- l += 1
114- return dfs(l, 0 , True )
112+ s = str (n)
113+ return dfs(0 , 0 , True )
115114```
116115
117116#### Java
118117
119118``` java
120119class Solution {
121- private int [] a = new int [12 ];
122- private int [][] dp = new int [12 ][12 ];
120+ private int m;
121+ private char [] s;
122+ private Integer [][] f;
123123
124124 public int countDigitOne (int n ) {
125- int len = 0 ;
126- while (n > 0 ) {
127- a[++ len] = n % 10 ;
128- n /= 10 ;
129- }
130- for (var e : dp) {
131- Arrays . fill(e, - 1 );
132- }
133- return dfs(len, 0 , true );
125+ s = String . valueOf(n). toCharArray();
126+ m = s. length;
127+ f = new Integer [m][m];
128+ return dfs(0 , 0 , true );
134129 }
135130
136- private int dfs (int pos , int cnt , boolean limit ) {
137- if (pos <= 0 ) {
131+ private int dfs (int i , int cnt , boolean limit ) {
132+ if (i >= m ) {
138133 return cnt;
139134 }
140- if (! limit && dp[pos ][cnt] != - 1 ) {
141- return dp[pos ][cnt];
135+ if (! limit && f[i ][cnt] != null ) {
136+ return f[i ][cnt];
142137 }
143- int up = limit ? a[pos] : 9 ;
138+ int up = limit ? s[i] - ' 0 ' : 9 ;
144139 int ans = 0 ;
145- for (int i = 0 ; i <= up; ++ i ) {
146- ans += dfs(pos - 1 , cnt + (i == 1 ? 1 : 0 ), limit && i == up);
140+ for (int j = 0 ; j <= up; ++ j ) {
141+ ans += dfs(i + 1 , cnt + (j == 1 ? 1 : 0 ), limit && j == up);
147142 }
148143 if (! limit) {
149- dp[pos ][cnt] = ans;
144+ f[i ][cnt] = ans;
150145 }
151146 return ans;
152147 }
@@ -158,35 +153,29 @@ class Solution {
158153``` cpp
159154class Solution {
160155public:
161- int a[ 12] ;
162- int dp[ 12] [ 12 ] ;
163-
164156 int countDigitOne(int n) {
165- int len = 0;
166- while (n) {
167- a[++len] = n % 10;
168- n /= 10;
169- }
170- memset (dp, -1, sizeof dp);
171- return dfs(len, 0, true);
172- }
173-
174- int dfs(int pos, int cnt, bool limit) {
175- if (pos <= 0) {
176- return cnt;
177- }
178- if (!limit && dp[pos][cnt] != -1) {
179- return dp[pos][cnt];
180- }
181- int ans = 0;
182- int up = limit ? a[pos] : 9;
183- for (int i = 0; i <= up; ++i) {
184- ans += dfs(pos - 1, cnt + (i == 1), limit && i == up);
185- }
186- if (!limit) {
187- dp[pos][cnt] = ans;
188- }
189- return ans;
157+ string s = to_string(n);
158+ int m = s.size();
159+ int f[ m] [ m ] ;
160+ memset(f, -1, sizeof(f));
161+ auto dfs = [ &] (auto&& dfs, int i, int cnt, bool limit) -> int {
162+ if (i >= m) {
163+ return cnt;
164+ }
165+ if (!limit && f[ i] [ cnt ] != -1) {
166+ return f[ i] [ cnt ] ;
167+ }
168+ int up = limit ? s[ i] - '0' : 9;
169+ int ans = 0;
170+ for (int j = 0; j <= up; ++j) {
171+ ans += dfs(dfs, i + 1, cnt + (j == 1), limit && j == up);
172+ }
173+ if (!limit) {
174+ f[ i] [ cnt ] = ans;
175+ }
176+ return ans;
177+ };
178+ return dfs(dfs, 0, 0, true);
190179 }
191180};
192181```
@@ -195,67 +184,103 @@ public:
195184
196185```go
197186func countDigitOne(n int) int {
198- a := make([]int, 12)
199- dp := make([][]int, 12)
200- for i := range dp {
201- dp[i] = make([]int, 12)
202- for j := range dp[i] {
203- dp[i][j] = -1
187+ s := strconv.Itoa(n)
188+ m := len(s)
189+ f := make([][]int, m)
190+ for i := range f {
191+ f[i] = make([]int, m)
192+ for j := range f[i] {
193+ f[i][j] = -1
204194 }
205195 }
206- l := 0
207- for n > 0 {
208- l++
209- a[l] = n % 10
210- n /= 10
211- }
212- var dfs func(int, int, bool) int
213- dfs = func(pos, cnt int, limit bool) int {
214- if pos <= 0 {
196+ var dfs func(i, cnt int, limit bool) int
197+ dfs = func(i, cnt int, limit bool) int {
198+ if i >= m {
215199 return cnt
216200 }
217- if !limit && dp[pos ][cnt] != -1 {
218- return dp[pos ][cnt]
201+ if !limit && f[i ][cnt] != -1 {
202+ return f[i ][cnt]
219203 }
220204 up := 9
221205 if limit {
222- up = a[pos]
206+ up = int(s[i] - '0')
223207 }
224208 ans := 0
225- for i := 0; i <= up; i ++ {
226- t := cnt
227- if i == 1 {
228- t++
209+ for j := 0; j <= up; j ++ {
210+ t := 0
211+ if j == 1 {
212+ t = 1
229213 }
230- ans += dfs(pos- 1, t, limit && i == up)
214+ ans += dfs(i+ 1, cnt+ t, limit && j == up)
231215 }
232216 if !limit {
233- dp[pos ][cnt] = ans
217+ f[i ][cnt] = ans
234218 }
235219 return ans
236220 }
237- return dfs(l, 0, true)
221+ return dfs(0, 0, true)
222+ }
223+ ```
224+
225+ #### TypeScript
226+
227+ ``` ts
228+ function countDigitOne(n : number ): number {
229+ const s = n .toString ();
230+ const m = s .length ;
231+ const f: number [][] = Array .from ({ length: m }, () => Array (m ).fill (- 1 ));
232+ const dfs = (i : number , cnt : number , limit : boolean ): number => {
233+ if (i >= m ) {
234+ return cnt ;
235+ }
236+ if (! limit && f [i ][cnt ] !== - 1 ) {
237+ return f [i ][cnt ];
238+ }
239+ const up = limit ? + s [i ] : 9 ;
240+ let ans = 0 ;
241+ for (let j = 0 ; j <= up ; ++ j ) {
242+ ans += dfs (i + 1 , cnt + (j === 1 ? 1 : 0 ), limit && j === up );
243+ }
244+ if (! limit ) {
245+ f [i ][cnt ] = ans ;
246+ }
247+ return ans ;
248+ };
249+ return dfs (0 , 0 , true );
238250}
239251```
240252
241253#### C#
242254
243255``` cs
244256public class Solution {
257+ private int m ;
258+ private char [] s ;
259+ private int ?[,] f ;
260+
245261 public int CountDigitOne (int n ) {
246- if (n <= 0 ) return 0 ;
247- if (n < 10 ) return 1 ;
248- return CountDigitOne (n / 10 - 1 ) * 10 + n / 10 + CountDigitOneOfN (n / 10 ) * (n % 10 + 1 ) + (n % 10 >= 1 ? 1 : 0 );
262+ s = n .ToString ().ToCharArray ();
263+ m = s .Length ;
264+ f = new int ?[m , m ];
265+ return Dfs (0 , 0 , true );
249266 }
250267
251- private int CountDigitOneOfN (int n ) {
252- var count = 0 ;
253- while (n > 0 )
254- {
255- if (n % 10 == 1 ) ++ count ;
256- n /= 10 ;
268+ private int Dfs (int i , int cnt , bool limit ) {
269+ if (i >= m ) {
270+ return cnt ;
257271 }
258- return count ;
272+ if (! limit && f [i , cnt ] != null ) {
273+ return f [i , cnt ].Value ;
274+ }
275+ int up = limit ? s [i ] - '0' : 9 ;
276+ int ans = 0 ;
277+ for (int j = 0 ; j <= up ; ++ j ) {
278+ ans += Dfs (i + 1 , cnt + (j == 1 ? 1 : 0 ), limit && j == up );
279+ }
280+ if (! limit ) {
281+ f [i , cnt ] = ans ;
282+ }
283+ return ans ;
259284 }
260285}
261286```
0 commit comments