3348. Smallest Divisible Digit Product II
- String
- Greedy
- Math
- Number Theory
- Enumeration
TIME
O(n)
SPACE
O(n)
Intuition
Digits 1..9 only carry the prime factors 2, 3, 5, 7. So first strip those primes out of t; if anything remains, no digit product can ever be divisible by t → "-1". What's left is a required exponent tuple (a2, a3, a5, a7) that the answer's digits must jointly supply.
Two building blocks:
- Minimal covering multiset. Cover the required exponents with the fewest, largest digits,
9 = 3^2,8 = 2^3,6 = 2*3, and forced5s /7s (only digit5gives a factor 5, only7gives a factor 7). Sorting the multiset ascending gives the lexicographically smallest arrangement, and its size is the minimum number of "significant" digits needed. - Smallest number of a given length that covers. Pad the front with
1s (they contribute nothing) and place the covering multiset at the end.
Approach
- Reduce
tby dividing out 2, 3, 5, 7 → exponents(a2,a3,a5,a7); leftover> 1→"-1". - Same length as
num:- If
numis zero-free and its digit product already covers the exponents → returnnum. - Otherwise find the largest position
ito bump: keep the (zero-free) prefixnum[0..i-1], set digitito the smallest value> num[i]such that the remaining exponents can still be covered by the suffix positions, then fill the suffix with1s + minimal covering digits. A larger branch position always yields a smaller result, so the first feasible one (scanning right to left) wins. The valid branch range is capped at the first0innum(a kept prefix must stay zero-free; the0itself can be bumped).
- If
- Fallback to a longer number: if no same-length answer exists, the answer has length
L = max(n+1, mincount)and is"1" * (L - mincount) + minimalDigitsthe smallest zero-free covering number of that length.
Complexity
- Time: O(n), cumulative prefix exponents, one right-to-left scan with O(1) work per position (8 candidate digits), suffix built once
- Space: O(n), prefix arrays and the output
Edge Cases
thas a prime factor other than 2/3/5/7 →"-1"(Example 3)numalready valid → return it (Example 2)numcontains0s → branch position is capped at the first0t = 1→ any zero-free number works → smallest zero-free>= num- Required
5s /7s force5/7digits (e.g.7^3→"777") - Use 64-bit for
t(up to10^14); digit exponents are small ints
Code
class Solution:
def smallestNumber(self, num: str, t: int) -> str:
"""
3348. Smallest Divisible Digit Product II
Time: O(n)
Space: O(n)
"""
# Reduce t to its 2/3/5/7 exponents; any leftover prime is impossible.
exp = {2: 0, 3: 0, 5: 0, 7: 0}
for p in (2, 3, 5, 7):
while t % p == 0:
t //= p
exp[p] += 1
if t != 1:
return "-1"
A2, A3, A5, A7 = exp[2], exp[3], exp[5], exp[7]
# Per-digit (2,3,5,7) exponent contributions.
C = {
1: (0, 0, 0, 0), 2: (1, 0, 0, 0), 3: (0, 1, 0, 0), 4: (2, 0, 0, 0),
5: (0, 0, 1, 0), 6: (1, 1, 0, 0), 7: (0, 0, 0, 1), 8: (3, 0, 0, 0),
9: (0, 2, 0, 0),
}
def leftover(r2: int, r3: int):
# smallest digits covering r2 twos (0..2) and r3 threes (0..1)
if r2 == 1 and r3 == 1:
return [6]
if r2 == 2 and r3 == 1:
return [2, 6]
if r2 == 1 and r3 == 0:
return [2]
if r2 == 2 and r3 == 0:
return [4]
if r2 == 0 and r3 == 1:
return [3]
return []
def build_digits(x2, x3, x5, x7):
n9, r3 = divmod(x3, 2)
n8, r2 = divmod(x2, 3)
ds = [9] * n9 + [8] * n8 + leftover(r2, r3) + [5] * x5 + [7] * x7
ds.sort()
return ds
def mincount(x2, x3, x5, x7):
n9, r3 = divmod(x3, 2)
n8, r2 = divmod(x2, 3)
return n9 + n8 + len(leftover(r2, r3)) + x5 + x7
S = build_digits(A2, A3, A5, A7)
S_str = "".join(map(str, S))
min_c = len(S)
n = len(num)
digs = [ord(c) - 48 for c in num]
# First zero index (kept prefix must be zero-free).
z = n
for i, d in enumerate(digs):
if d == 0:
z = i
break
# Cumulative exponent contribution of num[0..i-1] (valid for i <= z).
pref = [(0, 0, 0, 0)] * (n + 1)
cur = (0, 0, 0, 0)
for i in range(n):
if i < z:
c = C[digs[i]]
cur = (cur[0] + c[0], cur[1] + c[1], cur[2] + c[2], cur[3] + c[3])
pref[i + 1] = cur
def remaining(contrib):
return (max(0, A2 - contrib[0]), max(0, A3 - contrib[1]),
max(0, A5 - contrib[2]), max(0, A7 - contrib[3]))
# Candidate: num itself (zero-free and already covers t).
if z == n:
f = pref[n]
if f[0] >= A2 and f[1] >= A3 and f[2] >= A5 and f[3] >= A7:
return num
# Same length: bump at the largest feasible position.
hi = z if z < n else n - 1
for i in range(hi, -1, -1):
base = pref[i]
avail = n - 1 - i
for d in range(digs[i] + 1, 10):
cd = C[d]
contrib = (base[0] + cd[0], base[1] + cd[1], base[2] + cd[2], base[3] + cd[3])
r2, r3, r5, r7 = remaining(contrib)
need = mincount(r2, r3, r5, r7)
if need <= avail:
suf = build_digits(r2, r3, r5, r7)
suffix = "1" * (avail - need) + "".join(map(str, suf))
return num[:i] + str(d) + suffix
# No same-length answer: use the smallest longer covering number.
L = max(n + 1, min_c)
return "1" * (L - min_c) + S_str/**
* 3348. Smallest Divisible Digit Product II
* Time: O(n)
* Space: O(n)
*/
class Solution {
// Per-digit (2,3,5,7) exponent contributions, indexed by digit.
private static final int[][] C = {
{0, 0, 0, 0}, {0, 0, 0, 0}, {1, 0, 0, 0}, {0, 1, 0, 0}, {2, 0, 0, 0},
{0, 0, 1, 0}, {1, 1, 0, 0}, {0, 0, 0, 1}, {3, 0, 0, 0}, {0, 2, 0, 0},
};
private int A2, A3, A5, A7;
public String smallestNumber(String num, long t) {
// Reduce t to its 2/3/5/7 exponents; any leftover prime is impossible.
int[] e = new int[8];
for (int p : new int[]{2, 3, 5, 7}) {
while (t % p == 0) {
t /= p;
e[p]++;
}
}
if (t != 1) {
return "-1";
}
A2 = e[2]; A3 = e[3]; A5 = e[5]; A7 = e[7];
int[] S = buildDigits(A2, A3, A5, A7);
int minC = S.length;
int n = num.length();
int[] digs = new int[n];
for (int i = 0; i < n; i++) {
digs[i] = num.charAt(i) - '0';
}
// First zero index (kept prefix must be zero-free).
int z = n;
for (int i = 0; i < n; i++) {
if (digs[i] == 0) {
z = i;
break;
}
}
// Cumulative exponent contribution of num[0..i-1] (valid for i <= z).
int[][] pref = new int[n + 1][4];
for (int i = 0; i < n; i++) {
pref[i + 1] = pref[i];
if (i < z) {
int[] c = C[digs[i]];
pref[i + 1] = new int[]{pref[i][0] + c[0], pref[i][1] + c[1],
pref[i][2] + c[2], pref[i][3] + c[3]};
}
}
// Candidate: num itself (zero-free and already covers t).
if (z == n) {
int[] f = pref[n];
if (f[0] >= A2 && f[1] >= A3 && f[2] >= A5 && f[3] >= A7) {
return num;
}
}
// Same length: bump at the largest feasible position.
int hi = (z < n) ? z : n - 1;
for (int i = hi; i >= 0; i--) {
int[] base = pref[i];
int avail = n - 1 - i;
for (int d = digs[i] + 1; d <= 9; d++) {
int[] cd = C[d];
int r2 = Math.max(0, A2 - (base[0] + cd[0]));
int r3 = Math.max(0, A3 - (base[1] + cd[1]));
int r5 = Math.max(0, A5 - (base[2] + cd[2]));
int r7 = Math.max(0, A7 - (base[3] + cd[3]));
int need = mincount(r2, r3, r5, r7);
if (need <= avail) {
int[] suf = buildDigits(r2, r3, r5, r7);
StringBuilder sb = new StringBuilder();
sb.append(num, 0, i);
sb.append((char) ('0' + d));
for (int k = 0; k < avail - need; k++) {
sb.append('1');
}
for (int digit : suf) {
sb.append((char) ('0' + digit));
}
return sb.toString();
}
}
}
// No same-length answer: use the smallest longer covering number.
int L = Math.max(n + 1, minC);
StringBuilder sb = new StringBuilder();
for (int k = 0; k < L - minC; k++) {
sb.append('1');
}
for (int digit : S) {
sb.append((char) ('0' + digit));
}
return sb.toString();
}
/** Smallest digits covering r2 twos (0..2) and r3 threes (0..1). */
private int[] leftover(int r2, int r3) {
if (r2 == 1 && r3 == 1) return new int[]{6};
if (r2 == 2 && r3 == 1) return new int[]{2, 6};
if (r2 == 1 && r3 == 0) return new int[]{2};
if (r2 == 2 && r3 == 0) return new int[]{4};
if (r2 == 0 && r3 == 1) return new int[]{3};
return new int[]{};
}
/** Minimal covering multiset, sorted ascending. */
private int[] buildDigits(int x2, int x3, int x5, int x7) {
int n9 = x3 / 2, r3 = x3 % 2;
int n8 = x2 / 3, r2 = x2 % 3;
int[] lo = leftover(r2, r3);
int total = n9 + n8 + lo.length + x5 + x7;
int[] ds = new int[total];
int p = 0;
for (int i = 0; i < n9; i++) ds[p++] = 9;
for (int i = 0; i < n8; i++) ds[p++] = 8;
for (int v : lo) ds[p++] = v;
for (int i = 0; i < x5; i++) ds[p++] = 5;
for (int i = 0; i < x7; i++) ds[p++] = 7;
java.util.Arrays.sort(ds);
return ds;
}
private int mincount(int x2, int x3, int x5, int x7) {
int n9 = x3 / 2, r3 = x3 % 2;
int n8 = x2 / 3, r2 = x2 % 3;
return n9 + n8 + leftover(r2, r3).length + x5 + x7;
}
}