Skip to content

1012. Numbers With Repeated Digits 👍

  • Time: $O(\log n \cdot 2^{10} \cdot 2 \cdot 10)$
  • Space: $O(\log n \cdot 2^{10} \cdot 2)$
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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;
    vector<vector<vector<int>>> mem(
        digitSize + 1, vector<vector<int>>(1 << 10, vector<int>(2, -1)));
    return count(to_string(n), 0, 0, true, mem) - 1;  // - 0;
  }

 private:
  // Returns the number of special integers, considering the i-th digit, where
  // `used` is the bitmask of the used digits, and `isTight` indicates if the
  // current digit is tightly bound.
  int count(const string& s, int i, int used, bool isTight,
            vector<vector<vector<int>>>& mem) {
    if (i == s.length())
      return 1;
    if (mem[i][used][isTight] != -1)
      return mem[i][used][isTight];

    int res = 0;

    const int maxDigit = isTight ? s[i] - '0' : 9;
    for (int d = 0; d <= maxDigit; ++d) {
      // `d` is used.
      if (used >> d & 1)
        continue;
      // Use `d` now.
      const bool nextIsTight = isTight && (d == maxDigit);
      if (used == 0 && d == 0)  // Don't count leading 0s as used.
        res += count(s, i + 1, used, nextIsTight, mem);
      else
        res += count(s, i + 1, used | 1 << d, nextIsTight, mem);
    }

    return mem[i][used][isTight] = res;
  }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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;
    Integer[][][] mem = new Integer[digitSize + 1][1 << 10][2];
    return count(String.valueOf(n), 0, 0, true, mem) - 1; // - 0;
  }

  // Returns the number of special integers, considering the i-th digit, where
  // `used` is the bitmask of the used digits, and `isTight` indicates if the
  // current digit is tightly bound.
  private int count(final String s, int i, int used, boolean isTight, Integer[][][] mem) {
    if (i == s.length())
      return 1;
    if (mem[i][used][isTight ? 1 : 0] != null)
      return mem[i][used][isTight ? 1 : 0];

    int res = 0;

    final int maxDigit = isTight ? s.charAt(i) - '0' : 9;
    for (int d = 0; d <= maxDigit; ++d) {
      // `d` is used.
      if ((used >> d & 1) == 1)
        continue;
      // Use `d` now.
      final boolean nextIsTight = isTight && (d == maxDigit);
      if (used == 0 && d == 0) // Don't count leading 0s as used.
        res += count(s, i + 1, used, nextIsTight, mem);
      else
        res += count(s, i + 1, used | 1 << d, nextIsTight, mem);
    }

    return mem[i][used][isTight ? 1 : 0] = res;
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Solution:
  def numDupDigitsAtMostN(self, n: int) -> int:
    return n - self._countSpecialNumbers(n)

  # Same as 2376. Count Special Integers
  def _countSpecialNumbers(self, n: int) -> int:
    s = str(n)

    @functools.lru_cache(None)
    def dp(i: int, used: int, isTight: bool) -> int:
      """
      Returns the number of special integers, considering the i-th digit, where
      `used` is the bitmask of the used digits, and `isTight` indicates if the
      current digit is tightly bound.
      """
      if i == len(s):
        return 1

      res = 0

      maxDigit = int(s[i]) if isTight else 9
      for d in range(maxDigit + 1):
        # `d` is used.
        if used >> d & 1:
          continue
        # Use `d` now.
        nextIsTight = isTight and (d == maxDigit)
        if used == 0 and d == 0:  # Don't count leading 0s as used.
          res += dp(i + 1, used, nextIsTight)
        else:
          res += dp(i + 1, used | 1 << d, nextIsTight)

      return res

    return dp(0, 0, True) - 1  # - 0