1
1
## 方法一:从左到右爆搜
2
2
3
- ** 阅读提示** :需要至少做过一道数位 DP 题。
4
-
5
3
首先,由于数位乘积中的质因子只有 $2,3,5,7$,如果 $t$ 包含其他质因子,那么直接返回 $-1$。如果 $t$ 只包含质因子 $2,3,5,7$,那么答案一定存在。
6
4
7
5
下文把 $\textit{num}$ 简记为 $s$。其长度为 $n$。
8
6
9
7
例如 $s=123$,并假设答案的长度也为 $3$。
10
8
11
- 仿照 [ 数位 DP] ( https://www.bilibili.com/video/BV1rS4y1s721/?t=20m05s ) 的思路,写一个爆搜(回溯) :
9
+ 仿照 [ 数位 DP] ( https://www.bilibili.com/video/BV1rS4y1s721/?t=20m05s ) 的思路,写一个爆搜:
12
10
13
11
- 最高位如果填 $1$,那么第二位不能填 $1$(不然小于 $s$ 了),至少要填 $2$。
14
12
- 最高位如果填的数大于 $1$,那么第二位,以及第三位,填的数字不受到 $s$ 的约束,可以填 $[ 1,9] $ 中的任意数字。
15
13
16
- 这启发我们也像数位 DP 那样,在回溯的过程中 ,用一个参数 $\textit{isLimit}$ 表示「是否受到 $s$ 的约束」。
14
+ 这启发我们也像数位 DP 那样,在递归的过程中 ,用一个参数 $\textit{isLimit}$ 表示「是否受到 $s$ 的约束」。
17
15
18
16
如何判断所填数位之积是 $t$ 的倍数呢?
19
17
25
23
26
24
一般地,如果填的数字是 $d$,那么余下的数位,需要满足乘积是 $\dfrac{t}{\text{GCD}(t,d)}$ 的倍数。
27
25
28
- 综上所述,写一个带 $\textit{vis}$ 的爆搜(回溯) ,参数有:
26
+ 综上所述,写一个带 $\textit{vis}$ 的爆搜,参数有:
29
27
30
28
- $i$:表示当前填到 $s$ 的第 $i$ 个数位了。
31
29
- $t$:表示 $[ i,n-1] $ 所填数位,需要满足乘积是 $t$ 的倍数。
51
49
52
50
注意至少要添加 $1$ 个前导零,因为可能有 $s=999$ 这种情况,即使 $t=2$,答案($1112$)长度也比 $s$ 要长。
53
51
54
- 注意添加前导零会影响可以填入的数字,当 $\textit{isLimit}=\texttt{true}$ 且 $i < \textit{cnt}$ 时,我们可以填入 $0$。
52
+ 注意添加前导零会影响可以填入的数字,当 $\textit{isLimit}=\texttt{true}$ 且 $i < \textit{cnt}$ 时,我们可以填入 $0$。这和数位 DP 的「跳过不填数字」是一样的。
55
53
56
54
具体请看 [ 视频讲解] ( https://www.bilibili.com/video/BV1cgmBYqEhu/?t=28m31s ) ,欢迎点赞关注~
57
55
@@ -72,7 +70,7 @@ class Solution:
72
70
s = ' 0' * cnt + s
73
71
74
72
n = len (s)
75
- ans = [0 ] * n
73
+ ans = [' 0 ' ] * n
76
74
77
75
@cache # 仅仅作为 vis 标记使用
78
76
def dfs (i : int , t : int , is_limit : bool ) -> bool :
@@ -84,14 +82,14 @@ class Solution:
84
82
85
83
low = int (s[i]) if is_limit else 0
86
84
for d in range (max (low, 1 ), 10 ):
87
- ans[i] = d # 直接覆盖,无需恢复现场
88
85
if dfs(i + 1 , t // gcd(t, d), is_limit and d == low):
86
+ ans[i] = str (d)
89
87
return True
90
88
return False
91
89
92
90
dfs(0 , t, True )
93
91
dfs.cache_clear() # 防止爆内存
94
- return ' ' .join(map ( str , ans) ).lstrip(' 0' ) # 去掉前导零
92
+ return ' ' .join(ans).lstrip(' 0' ) # 去掉前导零
95
93
```
96
94
97
95
``` py [sol-Python3 写法二]
@@ -111,7 +109,7 @@ class Solution:
111
109
s = ' 0' * cnt + s
112
110
113
111
n = len (s)
114
- ans = [0 ] * n
112
+ ans = [' 0 ' ] * n
115
113
vis = [set () for _ in range (n)]
116
114
117
115
def dfs (i : int , t : int , is_limit : bool ) -> bool :
@@ -128,13 +126,13 @@ class Solution:
128
126
129
127
low = int (s[i]) if is_limit else 0
130
128
for d in range (max (low, 1 ), 10 ):
131
- ans[i] = d # 直接覆盖,无需恢复现场
132
129
if dfs(i + 1 , t // gcd(t, d), is_limit and d == low):
130
+ ans[i] = str (d)
133
131
return True
134
132
return False
135
133
136
134
dfs(0 , t, True )
137
- return ' ' .join(map ( str , ans) ).lstrip(' 0' ) # 去掉前导零
135
+ return ' ' .join(ans).lstrip(' 0' ) # 去掉前导零
138
136
```
139
137
140
138
``` java [sol-Java]
@@ -185,8 +183,8 @@ class Solution {
185
183
186
184
int low = isLimit ? s[i] - ' 0' : 0 ;
187
185
for (int d = Math . max(low, 1 ); d <= 9 ; d++ ) {
188
- ans[i] = (char ) (' 0' + d); // 直接覆盖,无需恢复现场
189
186
if (dfs(i + 1 , t / gcd(t, d), isLimit && d == low, cnt, s, ans, vis)) {
187
+ ans[i] = (char ) (' 0' + d);
190
188
return true ;
191
189
}
192
190
}
@@ -241,8 +239,8 @@ public:
241
239
242
240
int low = is_limit ? s[i] - '0' : 0;
243
241
for (int d = max(low, 1); d <= 9; d++) {
244
- ans[i] = '0' + d; // 直接覆盖,无需恢复现场
245
242
if (dfs(dfs, i + 1, t / gcd(t, d), is_limit && d == low)) {
243
+ ans[i] = '0' + d;
246
244
return true;
247
245
}
248
246
}
@@ -300,8 +298,8 @@ func smallestNumber(s string, t int64) string {
300
298
low = int (s[i] - ' 0' )
301
299
}
302
300
for d := max (low, 1 ); d <= 9 ; d++ {
303
- ans[i] = ' 0' + byte (d) // 直接覆盖,无需恢复现场
304
301
if dfs (i+1 , t/gcd (t, d), isLimit && d == low) {
302
+ ans[i] = ' 0' + byte (d)
305
303
return true
306
304
}
307
305
}
@@ -611,6 +609,10 @@ func gcd(a, b int) int {
611
609
- 时间复杂度:$\mathcal{O}(n + D\log^2 t)$,其中 $n$ 是 $s$ 的长度,$D=9$。分析四重循环的循环次数,如果从 $i=n-1$ 开始循环,$i$ 至多减少 $\mathcal{O}(\log t)$ 次,就一定能在右边填入 $\mathcal{O}(\log t)$ 个数字,所以 $j$ 的循环次数是 $\mathcal{O}(\log t)$。而如果 $i$ 远小于 $n-1$,则一定能填入数字,$j$ 的循环次数是 $\mathcal{O}(n)$。
612
610
- 空间复杂度:$\mathcal{O}(n)$。
613
611
612
+ ## 相似题目
613
+
614
+ - [ 3260. 找出最大的 N 位 K 回文数] ( https://leetcode.cn/problems/find-the-largest-palindrome-divisible-by-k/ ) 2370
615
+
614
616
## 分类题单
615
617
616
618
[ 如何科学刷题?] ( https://leetcode.cn/circle/discuss/RvFUtj/ )
0 commit comments