Numbers With Repeated Digits

hard digit dp bitmask combinatorics

Problem

Given an integer n, return the number of positive integers in the range [1, n] that have at least one repeated digit.

Inputn = 100
Output10
The numbers with a repeated digit in [1, 100] are 11, 22, 33, 44, 55, 66, 77, 88, 99 and 100 — exactly 10 of them.

def num_dup_digits_at_most_n(n):
    digits = [int(c) for c in str(n)]
    L = len(digits)
    # Count integers in [1, n] with ALL DISTINCT digits.
    unique = 0
    # Part 1: numbers with fewer digits than n.
    for k in range(1, L):
        cur = 9
        for j in range(1, k):
            cur *= (10 - j)
        unique += cur
    # Part 2: same length as n, prefix-by-prefix.
    used = set()
    for i, d in enumerate(digits):
        lo = 1 if i == 0 else 0
        for x in range(lo, d):
            if x in used:
                continue
            rest = 1
            for j in range(L - i - 1):
                rest *= (10 - i - 1 - j)
            unique += rest
        if d in used:
            break
        used.add(d)
    else:
        unique += 1  # n itself has all distinct digits
    return n - unique
function numDupDigitsAtMostN(n) {
  const digits = String(n).split("").map(Number);
  const L = digits.length;
  let unique = 0;
  // Part 1: numbers with fewer digits than n.
  for (let k = 1; k < L; k++) {
    let cur = 9;
    for (let j = 1; j < k; j++) cur *= (10 - j);
    unique += cur;
  }
  // Part 2: same length as n, prefix-by-prefix.
  const used = new Set();
  let broke = false;
  for (let i = 0; i < L; i++) {
    const d = digits[i];
    const lo = i === 0 ? 1 : 0;
    for (let x = lo; x < d; x++) {
      if (used.has(x)) continue;
      let rest = 1;
      for (let j = 0; j < L - i - 1; j++) rest *= (10 - i - 1 - j);
      unique += rest;
    }
    if (used.has(d)) { broke = true; break; }
    used.add(d);
  }
  if (!broke) unique += 1; // n itself has all distinct digits
  return n - unique;
}
class Solution {
    public int numDupDigitsAtMostN(int n) {
        char[] s = String.valueOf(n).toCharArray();
        int L = s.length, unique = 0;
        // Part 1: numbers with fewer digits than n.
        for (int k = 1; k < L; k++) {
            int cur = 9;
            for (int j = 1; j < k; j++) cur *= (10 - j);
            unique += cur;
        }
        // Part 2: same length as n, prefix-by-prefix.
        boolean[] used = new boolean[10];
        boolean broke = false;
        for (int i = 0; i < L; i++) {
            int d = s[i] - '0';
            int lo = (i == 0) ? 1 : 0;
            for (int x = lo; x < d; x++) {
                if (used[x]) continue;
                int rest = 1;
                for (int j = 0; j < L - i - 1; j++) rest *= (10 - i - 1 - j);
                unique += rest;
            }
            if (used[d]) { broke = true; break; }
            used[d] = true;
        }
        if (!broke) unique += 1; // n itself has all distinct digits
        return n - unique;
    }
}
int numDupDigitsAtMostN(int n) {
    string s = to_string(n);
    int L = s.size(), unique = 0;
    // Part 1: numbers with fewer digits than n.
    for (int k = 1; k < L; k++) {
        int cur = 9;
        for (int j = 1; j < k; j++) cur *= (10 - j);
        unique += cur;
    }
    // Part 2: same length as n, prefix-by-prefix.
    bool used[10] = {false};
    bool broke = false;
    for (int i = 0; i < L; i++) {
        int d = s[i] - '0';
        int lo = (i == 0) ? 1 : 0;
        for (int x = lo; x < d; x++) {
            if (used[x]) continue;
            int rest = 1;
            for (int j = 0; j < L - i - 1; j++) rest *= (10 - i - 1 - j);
            unique += rest;
        }
        if (used[d]) { broke = true; break; }
        used[d] = true;
    }
    if (!broke) unique += 1; // n itself has all distinct digits
    return n - unique;
}
Time: O(D²) Space: O(D)