Given an integer n
, return the number of positive integers in the range [1, n]
that have at least one repeated digit.
Example 1:
Input: n = 20 Output: 1 Explanation: The only positive number (<= 20) with at least 1 repeated digit is 11.
Example 2:
Input: n = 100 Output: 10 Explanation: The positive numbers (<= 100) with atleast 1 repeated digit are 11, 22, 33, 44, 55, 66, 77, 88, 99, and 100.
Example 3:
Input: n = 1000 Output: 262
Constraints:
1 <= n <= 109
This problem asks to find the count of numbers within the range [1, n] that contain at least one repeated digit. The most efficient approach is to use dynamic programming with state compression. Instead of directly counting numbers with repeated digits, we'll count numbers without repeated digits and subtract that from the total numbers (n).
Approach:
Digit DP: We'll use a recursive function (dfs
in the code) that explores the digits of the numbers from left to right. The function intelligently avoids exploring unnecessary branches by considering limits and leading zeros.
State Compression: We use a bitmask (mask
) to track which digits have already been used. Each bit in the mask represents a digit (0-9). A bit set to 1 means the digit is already present, and 0 means it's not used yet.
Memoization: To avoid redundant calculations, we use memoization (dynamic programming). The dfs
function's results are stored in a cache (f
in the code) to avoid recomputing the same subproblems.
Base Case: The base case for dfs
is when all digits of the number have been processed. If there were no leading zeros and no repeated digits, we count this as a valid number (returning 1). Otherwise, we return 0.
Recursive Step: The dfs
function recursively explores each possible digit (0-9) at each position. It checks:
n
and previous digits).mask
).Final Result: The function numDupDigitsAtMostN(n)
calls dfs
to count numbers without repeated digits and then subtracts this count from n
to get the count of numbers with at least one repeated digit.
Time Complexity: O(log n * 210 * 10) ≈ O(10240 * log n). The logarithmic factor comes from the number of digits in n. The 210 represents the possible states of the bitmask (10 digits), and the factor of 10 accounts for the loop iterating through possible digits. Since the input is capped at 109, the logarithmic factor doesn't significantly impact the overall time complexity. Effectively, the runtime is almost constant for all inputs.
Space Complexity: O(log n * 210) ≈ O(1024 * log n). This is due to the memoization table (f
).
Code Examples (Python):
from functools import cache
class Solution:
def numDupDigitsAtMostN(self, n: int) -> int:
s = str(n)
m = len(s)
@cache
def dfs(i, mask, lead, limit):
if i == m:
return lead ^ 1 # 1 if no leading zeros and no repeated digits, 0 otherwise
up = int(s[i]) if limit else 9
ans = 0
for d in range(up + 1):
if lead and d == 0:
ans += dfs(i + 1, mask, True, limit and d == up)
elif (mask >> d) & 1 == 0:
ans += dfs(i + 1, mask | (1 << d), False, limit and d == up)
return ans
return n - dfs(0, 0, True, True)
The code in other languages (Java, C++, Go, TypeScript) follows the same logic with minor syntactic differences to adapt to each language's features. The core algorithm remains consistent across all implementations.