LeetCode Solutions
1012. Numbers With Repeated Digits
Time: $O(\log n)$ Space: $O(\log n \cdot 2^{10})$
class Solution {
public:
int numDupDigitsAtMostN(int n) {
return n - countSpecialNumbers(n);
}
private:
// Same as 2376. Count Special Integers
int countSpecialNumbers(int n) {
const int digitSize = log10(n) + 1;
// dp[i][j][k] := # of special integers that belong to the interval
// [0, 10^i] with `usedMask` j, where k is 0/1 tight constraint
dp.resize(digitSize + 1, vector<vector<int>>(1 << 10, vector<int>(2, -1)));
return count(to_string(n), digitSize, 0, true) - 1; // - 0;
}
vector<vector<vector<int>>> dp;
int count(const string& s, int digitSize, int usedMask, bool isTight) {
if (digitSize == 0)
return 1;
if (dp[digitSize][usedMask][isTight] != -1)
return dp[digitSize][usedMask][isTight];
int ans = 0;
const int maxDigit = isTight ? s[s.length() - digitSize] - '0' : 9;
for (int digit = 0; digit <= maxDigit; ++digit) {
// `digit` is used
if (usedMask >> digit & 1)
continue;
// Use `digit` now
const bool nextIsTight = isTight && (digit == maxDigit);
if (usedMask == 0 && digit == 0) // don't count leading 0s as used
ans += count(s, digitSize - 1, usedMask, nextIsTight);
else
ans += count(s, digitSize - 1, usedMask | 1 << digit, nextIsTight);
}
return dp[digitSize][usedMask][isTight] = ans;
}
};
class Solution {
public int numDupDigitsAtMostN(int n) {
return n - countSpecialNumbers(n);
}
// Same as 2376. Count Special Integers
private int countSpecialNumbers(int n) {
final int digitSize = (int) Math.log10(n) + 1;
// dp[i][j][k] := # of special integers that belong to the interval
// [0, 10^i] with `usedMask` j, where k is 0/1 tight constraint
dp = new Integer[digitSize + 1][1 << 10][2];
return count(String.valueOf(n), digitSize, 0, true) - 1; // - 0;
}
private Integer[][][] dp;
private int count(final String s, int digitSize, int usedMask, boolean isTight) {
if (digitSize == 0)
return 1;
if (dp[digitSize][usedMask][isTight ? 1 : 0] != null)
return dp[digitSize][usedMask][isTight ? 1 : 0];
int ans = 0;
final int maxDigit = isTight ? s.charAt(s.length() - digitSize) - '0' : 9;
for (int digit = 0; digit <= maxDigit; ++digit) {
// `digit` is used
if ((usedMask >> digit & 1) == 1)
continue;
// Use `digit` now
final boolean nextIsTight = isTight && (digit == maxDigit);
if (usedMask == 0 && digit == 0) // don't count leading 0s as used
ans += count(s, digitSize - 1, usedMask, nextIsTight);
else
ans += count(s, digitSize - 1, usedMask | 1 << digit, nextIsTight);
}
return dp[digitSize][usedMask][isTight ? 1 : 0] = ans;
}
}
class Solution:
def numDupDigitsAtMostN(self, n: int) -> int:
return n - self._countSpecialNumbers(n)
def _countSpecialNumbers(self, n: int) -> int:
s = str(n)
digitSize = int(log10(n)) + 1
# Dp(i, j, k) := # Of special integers that beto the interval
# [0, 10^i] with `usedMask` j, where k is 0/1 tight constraint
@functools.lru_cache(None)
def dp(digitSize: int, usedMask: int, isTight: bool) -> int:
if digitSize == 0:
return 1
ans = 0
maxDigit = ord(s[len(s) - digitSize]) - ord('0') if isTight else 9
for digit in range(maxDigit + 1):
# `digit` is used
if usedMask >> digit & 1:
continue
# Use `digit` now
nextIsTight = isTight and (digit == maxDigit)
if usedMask == 0 and digit == 0: # don't count leading 0s as used
ans += dp(digitSize - 1, usedMask, nextIsTight)
else:
ans += dp(digitSize - 1, usedMask | 1 << digit, nextIsTight)
return ans
return dp(digitSize, 0, True) - 1 # - 0