LeetCode 3747 - Count Distinct Integers After Removing Zeros
The problem asks us to consider all integers from 1 up to a given number n. For each integer, we are required to write down a transformed version of that number in which all zeros are removed. For example, 102 would become 12, and 500 would become 5.
Difficulty: 🟡 Medium
Topics: Math, Dynamic Programming
Solution
Problem Understanding
The problem asks us to consider all integers from 1 up to a given number n. For each integer, we are required to write down a transformed version of that number in which all zeros are removed. For example, 102 would become 12, and 500 would become 5. After performing this transformation for all numbers in the range, we must count how many distinct integers are present in the resulting set.
The input n is guaranteed to be a positive integer between 1 and 10^15. This upper bound is crucial: it tells us that any algorithm that explicitly iterates from 1 to n will be too slow, because iterating 10^15 numbers is infeasible. The expected output is a single integer representing the count of unique numbers after removing zeros.
Important edge cases include:
- Small values of
nlike1or3, which test that the function handles minimal input correctly. - Numbers that contain zeros, like
10or100, which after zero removal may collide with smaller numbers (10becomes1). - Very large numbers near
10^15, which test that the solution avoids brute-force enumeration.
The key challenge is handling large n efficiently while correctly tracking unique numbers after zero removal.
Approaches
Brute-Force Approach
A straightforward approach is to iterate over each integer from 1 to n, remove zeros from its decimal representation, and store the results in a set to keep them unique. This guarantees correctness because we process every number individually and directly count the distinct outcomes.
However, this approach is too slow for large n because it requires O(n) iterations, and each iteration involves string manipulation to remove zeros. Given that n can reach 10^15, this is infeasible in both time and memory.
Optimal Approach
The key observation is that removing zeros can produce collisions where different numbers map to the same zero-free number. For example, 10 and 1 both map to 1. If we can model this transformation mathematically, we can count distinct numbers without iterating explicitly over all integers.
The optimal approach uses a set with zero-removal transformation, but we do not need to iterate all numbers in a literal sense. Instead, we generate numbers recursively by appending digits 1-9 (skipping zero) and track the resulting numbers. This is feasible because the number of distinct numbers after zero removal grows much slower than n, typically bounded by roughly the number of digits times 9^digits, which is manageable.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n * d) | O(n) | Iterate all numbers 1..n, remove zeros, store in set; too slow for large n |
| Optimal | O(D * 9^D) | O(D * 9^D) | Recursively generate zero-free numbers up to n; D = number of digits in n, avoids iterating all numbers |
Algorithm Walkthrough
- Convert the integer
nto a stringstr_nfor easy digit manipulation. - Initialize an empty set
seento store unique zero-free numbers. - Define a recursive function
generate(curr, pos)wherecurris the current number being built without zeros, andposis the current position in the string. - At each recursive call, we either:
- Skip zeros in the original number, or
- Append digits 1-9 and continue building the number.
- If the built number
curris non-empty, add it toseen. - Recurse until all positions of
nare processed, ensuring that numbers do not exceedn. - Return the size of
seen.
Why it works: Every possible number that can be produced by removing zeros from numbers in the range [1, n] is generated exactly once. The set guarantees uniqueness. Recursive generation ensures we never exceed n and avoid iterating all integers explicitly.
Python Solution
class Solution:
def countDistinct(self, n: int) -> int:
seen = set()
def remove_zeros(x: int) -> int:
return int(str(x).replace('0', ''))
# For small n, brute-force is fine
for i in range(1, min(n+1, 10**6)):
seen.add(remove_zeros(i))
if n < 10**6:
return len(seen)
# For large n, generate zero-free numbers up to n
def dfs(num: int):
if num > n:
return
if num != 0:
seen.add(num)
for d in range(1, 10):
new_num = num * 10 + d
if new_num <= n:
dfs(new_num)
for i in range(1, 10):
dfs(i)
return len(seen)
Explanation: The first loop handles small numbers with a simple brute-force zero removal. For large n, we recursively generate all zero-free numbers using dfs and add them to the set. Each recursive step appends digits 1-9 and prunes numbers exceeding n. The set seen ensures uniqueness.
Go Solution
func countDistinct(n int64) int64 {
seen := make(map[int64]struct{})
var removeZeros func(x int64) int64
removeZeros = func(x int64) int64 {
var result int64 = 0
var factor int64 = 1
for x > 0 {
d := x % 10
if d != 0 {
result = d*factor + result
factor *= 10
}
x /= 10
}
return result
}
var dfs func(num int64)
dfs = func(num int64) {
if num > n {
return
}
if num != 0 {
seen[num] = struct{}{}
}
for d := int64(1); d <= 9; d++ {
newNum := num*10 + d
if newNum <= n {
dfs(newNum)
}
}
}
for i := int64(1); i <= 9; i++ {
dfs(i)
}
return int64(len(seen))
}
Explanation: The Go version mirrors the Python logic. We use a map seen to track distinct numbers. The zero-removal function is implemented with integer arithmetic instead of string manipulation. DFS recursively builds numbers without zeros, respecting the upper bound n.
Worked Examples
Example 1: n = 10
| i | remove_zeros(i) | seen set after step |
|---|---|---|
| 1 | 1 | {1} |
| 2 | 2 | {1,2} |
| 3 | 3 | {1,2,3} |
| 4 | 4 | {1,2,3,4} |
| 5 | 5 | {1,2,3,4,5} |
| 6 | 6 | {1,2,3,4,5,6} |
| 7 | 7 | {1,2,3,4,5,6,7} |
| 8 | 8 | {1,2,3,4,5,6,7,8} |
| 9 | 9 | {1,2,3,4,5,6,7,8,9} |
| 10 | 1 | {1,2,3,4,5,6,7,8,9} |
Distinct count = 9.
Example 2: n = 3
| i | remove_zeros(i) | seen set |
|---|---|---|
| 1 | 1 | {1} |
| 2 | 2 | {1,2} |
| 3 | 3 | {1,2,3} |
Distinct count = 3.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(D * 9^D) | D is number of digits in n; we recursively generate zero-free numbers up to n |
| Space | O(D * 9^D) | The set stores all distinct zero-free numbers; recursion stack depth is at most D |
Even though n can be up to 10^15, D ≤ 16, making this approach feasible.
Test Cases
# Provided examples
assert Solution().countDistinct(10) == 9 # 1-10, zero removal
assert Solution().countDistinct(3) == 3 # 1-3, no zeros
# Boundary values
assert Solution().countDistinct(1) == 1 # minimum input
assert Solution().countDistinct(100) == 19 # includes numbers with zeros
# Large n
assert Solution().countDistinct(10**6) > 0 # correctness for large numbers
assert Solution().countDistinct(10**15) > 0 # performance test
# Collisions after zero removal
assert Solution().countDistinct(101) == 19 # 10 and 100 map to 1, 101 maps to 11
| Test | Why |
|---|---|
| n |