@@ -61,32 +61,217 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3700-3799/3747.Co
6161
6262<!-- solution:start -->
6363
64- ### 方法一
64+ ### 方法一:数位 DP
65+
66+ 题目实际上是要我们统计区间 $[ 1, n] $ 之间,数字中不含 0 的整数个数。我们可以使用数位 DP 来解决这个问题。
67+
68+ 我们设计一个函数 $\text{dfs}(i, \text{zero}, \text{lead}, \text{limit})$,表示当前处理到数字的第 $i$ 位,用 $\text{zero}$ 表示当前数字中是否已经出现过非零数字,用 $\text{lead}$ 表示当前是否还在处理前导零,而 $\text{limit}$ 表示当前数字是否受上界限制的方案数。答案为 $\text{dfs}(0, 0, 1, 1)$。
69+
70+ 函数 $\text{dfs}(i, \text{zero}, \text{lead}, \text{limit})$ 中,如果 $i$ 大于等于数字的长度,那么我们就可以判断 $\text{zero}$ 和 $\text{lead}$,如果 $\text{zero}$ 为假且 $\text{lead}$ 为假,说明当前数字中不含 0,我们就返回 $1$,否则返回 $0$。
71+
72+ 对于 $\text{dfs}(i, \text{zero}, \text{lead}, \text{limit})$,我们可以枚举当前数位 $d$ 的值,然后递归计算 $\text{dfs}(i+1, \text{nxt\_ zero}, \text{nxt\_ lead}, \text{nxt\_ limit})$,其中 $\text{nxt\_ zero}$ 表示当前数字中是否已经出现过非零数字,$\text{nxt\_ lead}$ 表示当前是否还在处理前导零,而 $\text{nxt\_ limit}$ 表示当前数字是否受上界限制。如果 $\text{limit}$ 为真,那么 $up$ 就是当前数位的上界,否则 $up$ 为 $9$。
73+
74+ 时间复杂度 $O(\log_ {10} n \times D)$,空间复杂度 $O(\log_ {10} n)$。其中 $D$ 表示数字 0 到 9 的个数。
6575
6676<!-- tabs:start -->
6777
6878#### Python3
6979
7080``` python
71-
81+ class Solution :
82+ def countDistinct (self , n : int ) -> int :
83+ @cache
84+ def dfs (i : int , zero : bool , lead : bool , lim : bool ) -> int :
85+ if i >= len (s):
86+ return 1 if (not zero and not lead) else 0
87+ up = int (s[i]) if lim else 9
88+ ans = 0
89+ for j in range (up + 1 ):
90+ nxt_zero = zero or (j == 0 and not lead)
91+ nxt_lead = lead and j == 0
92+ nxt_lim = lim and j == up
93+ ans += dfs(i + 1 , nxt_zero, nxt_lead, nxt_lim)
94+ return ans
95+
96+ s = str (n)
97+ return dfs(0 , False , True , True )
7298```
7399
74100#### Java
75101
76102``` java
77-
103+ class Solution {
104+ private char [] s;
105+ private Long [][][][] f;
106+
107+ public long countDistinct (long n ) {
108+ s = String . valueOf(n). toCharArray();
109+ f = new Long [s. length][2 ][2 ][2 ];
110+ return dfs(0 , 0 , 1 , 1 );
111+ }
112+
113+ private long dfs (int i , int zero , int lead , int limit ) {
114+ if (i == s. length) {
115+ return (zero == 0 && lead == 0 ) ? 1 : 0 ;
116+ }
117+
118+ if (limit == 0 && f[i][zero][lead][limit] != null ) {
119+ return f[i][zero][lead][limit];
120+ }
121+
122+ int up = limit == 1 ? s[i] - ' 0' : 9 ;
123+ long ans = 0 ;
124+ for (int d = 0 ; d <= up; d++ ) {
125+ int nxtZero = zero == 1 || (d == 0 && lead == 0 ) ? 1 : 0 ;
126+ int nxtLead = (lead == 1 && d == 0 ) ? 1 : 0 ;
127+ int nxtLimit = (limit == 1 && d == up) ? 1 : 0 ;
128+ ans += dfs(i + 1 , nxtZero, nxtLead, nxtLimit);
129+ }
130+
131+ if (limit == 0 ) {
132+ f[i][zero][lead][limit] = ans;
133+ }
134+ return ans;
135+ }
136+ }
78137```
79138
80139#### C++
81140
82141``` cpp
83-
142+ class Solution {
143+ public:
144+ long long countDistinct(long long n) {
145+ string s = to_string(n);
146+ int m = s.size();
147+ static long long f[ 20] [ 2 ] [ 2] [ 2 ] ;
148+ memset(f, -1, sizeof(f));
149+
150+ auto dfs = [&](this auto&& dfs, int i, int zero, int lead, int limit) -> long long {
151+ if (i == m) {
152+ return (zero == 0 && lead == 0) ? 1 : 0;
153+ }
154+ if (!limit && f[i][zero][lead][limit] != -1 ) {
155+ return f[i][zero][lead][limit];
156+ }
157+
158+ int up = limit ? (s[i] - '0') : 9;
159+ long long ans = 0;
160+ for (int d = 0; d <= up; d++) {
161+ int nxtZero = zero || (d == 0 && !lead);
162+ int nxtLead = lead && d == 0;
163+ int nxtLimit = limit && d == up;
164+ ans += dfs(i + 1, nxtZero, nxtLead, nxtLimit);
165+ }
166+
167+ if (!limit) f[i][zero][lead][limit] = ans;
168+ return ans;
169+ };
170+
171+ return dfs(0, 0, 1, 1);
172+ }
173+ };
84174```
85175
86176#### Go
87177
88178``` go
179+ func countDistinct (n int64 ) int64 {
180+ s := []byte (fmt.Sprint (n))
181+ m := len (s)
182+ var f [20 ][2 ][2 ][2 ]int64
183+ for i := range f {
184+ for j := range f[i] {
185+ for k := range f[i][j] {
186+ for t := range f[i][j][k] {
187+ f[i][j][k][t] = -1
188+ }
189+ }
190+ }
191+ }
192+
193+ var dfs func (i, zero, lead, limit int ) int64
194+ dfs = func (i, zero, lead, limit int ) int64 {
195+ if i == m {
196+ if zero == 0 && lead == 0 {
197+ return 1
198+ }
199+ return 0
200+ }
201+
202+ if limit == 0 && f[i][zero][lead][limit] != -1 {
203+ return f[i][zero][lead][limit]
204+ }
205+
206+ up := 9
207+ if limit == 1 {
208+ up = int (s[i] - ' 0' )
209+ }
210+
211+ var ans int64 = 0
212+ for d := 0 ; d <= up; d++ {
213+ nxtZero := zero
214+ if d == 0 && lead == 0 {
215+ nxtZero = 1
216+ }
217+ nxtLead := 0
218+ if lead == 1 && d == 0 {
219+ nxtLead = 1
220+ }
221+ nxtLimit := 0
222+ if limit == 1 && d == up {
223+ nxtLimit = 1
224+ }
225+ ans += dfs (i+1 , nxtZero, nxtLead, nxtLimit)
226+ }
227+
228+ if limit == 0 {
229+ f[i][zero][lead][limit] = ans
230+ }
231+ return ans
232+ }
233+
234+ return dfs (0 , 0 , 1 , 1 )
235+ }
236+ ```
89237
238+ #### TypeScript
239+
240+ ``` ts
241+ function countDistinct(n : number ): number {
242+ const s = n .toString ();
243+ const m = s .length ;
244+
245+ const f: number [][][][] = Array .from ({ length: m }, () =>
246+ Array .from ({ length: 2 }, () => Array .from ({ length: 2 }, () => Array (2 ).fill (- 1 ))),
247+ );
248+
249+ const dfs = (i : number , zero : number , lead : number , limit : number ): number => {
250+ if (i === m ) {
251+ return zero === 0 && lead === 0 ? 1 : 0 ;
252+ }
253+
254+ if (limit === 0 && f [i ][zero ][lead ][limit ] !== - 1 ) {
255+ return f [i ][zero ][lead ][limit ];
256+ }
257+
258+ const up = limit === 1 ? parseInt (s [i ]) : 9 ;
259+ let ans = 0 ;
260+ for (let d = 0 ; d <= up ; d ++ ) {
261+ const nxtZero = zero === 1 || (d === 0 && lead === 0 ) ? 1 : 0 ;
262+ const nxtLead = lead === 1 && d === 0 ? 1 : 0 ;
263+ const nxtLimit = limit === 1 && d === up ? 1 : 0 ;
264+ ans += dfs (i + 1 , nxtZero , nxtLead , nxtLimit );
265+ }
266+
267+ if (limit === 0 ) {
268+ f [i ][zero ][lead ][limit ] = ans ;
269+ }
270+ return ans ;
271+ };
272+
273+ return dfs (0 , 0 , 1 , 1 );
274+ }
90275```
91276
92277<!-- tabs:end -->
0 commit comments