Count Special Integers
HardPrompt
We call a positive integer special if all of its digits are distinct.
Given a positive integer n, return the number of special integers that belong to the interval [1, n].
Example 1:
Input: n = 20
Output: 19
Explanation: All the integers from 1 to 20, except 11, are special. Thus, there are 19 special integers.Example 2:
Input: n = 5
Output: 5
Explanation: All the integers from 1 to 5 are special.Example 3:
Input: n = 135
Output: 110
Explanation: There are 110 integers from 1 to 135 that are special.
Some of the integers that are not special are: 22, 114, and 131.
Constraints:
1 <= n <= 2 * 109
Approaches
2 approaches with complexity analysis and trade-offs.
A much more efficient method is to use digit dynamic programming, which builds the count based on the digits of n. Instead of checking every number, we can count the number of valid special integers mathematically. We can count the special integers with fewer digits than n and then count the special integers with the same number of digits as n but are less than or equal to n.
Algorithm
The problem is to count special numbers up to n. This can be solved using digit dynamic programming and combinatorics. Let S be the string representation of n and L be its length.
The count is divided into two parts:
-
Count special numbers with fewer digits than
n:- Iterate for each length
dfrom 1 toL-1. - For a
d-digit number, the first digit has 9 choices (1-9). The remainingd-1digits are a permutation ofd-1digits chosen from the 9 remaining available digits (0-9 excluding the first digit). - The count for length
dis9 * P(9, d-1), whereP(m, k)is the number of k-permutations of m. - Sum these counts for all
d < L.
- Iterate for each length
-
Count special numbers with the same number of digits as
nand are less than or equal ton:- Iterate through the digits of
nfrom left to right (indexifrom 0 toL-1). - Maintain a set of digits
seenin the prefix. - At each position
i, consider placing a digitdthat is smaller than the digitn[i]. - For each such valid
d(not 0 for the first position and not inseen), the remainingL-1-ipositions can be filled inP(10 - (i+1), L-1-i)ways. Add this to the total count. - After checking smaller digits, check the digit
n[i]itself. Ifn[i]is already inseen, it meansnand any other number with this prefix is not special. We must stop and return the current count. - If
n[i]is not inseen, add it toseenand proceed to the next positioni+1.
- Iterate through the digits of
-
Final check for
n:- If the loop completes, it means
nitself is a special number. Increment the final count by 1.
- If the loop completes, it means
Walkthrough
Let's illustrate with n = 135. The string is S = "135" and length L = 3.
Part 1: Count special numbers with length < 3
- 1-digit numbers: 9 (1-9).
- 2-digit numbers: The first digit has 9 choices (1-9). The second has 9 choices (0-9, excluding the first). Total:
9 * 9 = 81. - Total for length < 3:
9 + 81 = 90.
Part 2: Count 3-digit special numbers <= 135
- We iterate through
S = "135". - Position 0 (hundreds):
S[0] = '1'. We can't place a smaller digit (since it would have to be 0, which is not allowed as the first digit). So we must place '1'. Mark '1' as seen. - Position 1 (tens):
S[1] = '3'. We can place digits smaller than 3.- Try
d = 0: '0' is not seen. We form10_. The last digit has 8 choices (0-9, excluding 1 and 0). Add 8 to count. - Try
d = 2: '2' is not seen. We form12_. The last digit has 8 choices. Add 8 to count. - Now, we must place '3'. Mark '3' as seen.
- Try
- Position 2 (ones):
S[2] = '5'. We can place digits smaller than 5.- Try
d = 0, 2, 4(1 and 3 are seen). For each, we form130,132,134. These are 3 numbers. Add 3 to count.
- Try
- The loop finishes. Since
135itself has distinct digits, we add 1 for it. - Total for 3-digit numbers:
8 + 8 + 3 + 1 = 20.
Grand Total: 90 + 20 = 110.
Here is the code for this approach:
class Solution { public int countSpecialNumbers(int n) { String s = String.valueOf(n); int L = s.length(); int res = 0; // Count special numbers with length < L for (int d = 1; d < L; d++) { res += 9 * permutations(9, d - 1); } // Count special numbers with length L and <= n boolean[] seen = new boolean[10]; for (int i = 0; i < L; i++) { int digit = s.charAt(i) - '0'; for (int d = (i == 0) ? 1 : 0; d < digit; d++) { if (!seen[d]) { res += permutations(10 - (i + 1), L - 1 - i); } } if (seen[digit]) { return res; // Duplicate digit found in n's prefix } seen[digit] = true; } // If we reach here, n itself is a special number res++; return res; } // Calculates P(m, n) = m! / (m-n)! private int permutations(int m, int n) { if (n > m) return 0; int res = 1; for (int i = 0; i < n; i++) { res *= (m - i); } return res; }}Complexity
Time
O((log10(n))^2). The number of digits `L` is O(log10(n)). The first loop runs `L` times, and the second loop runs `L` times with an inner loop of constant size (10). The `permutations` function also takes O(L) time. This results in a very fast solution.
Space
O(log10(n)). We use a string to store `n` and a boolean array of size 10. The space is dominated by the string representation of `n`.
Trade-offs
Pros
Extremely efficient and fast, easily passing the given constraints.
The time complexity depends only on the number of digits in
n, not its magnitude.
Cons
The logic is more complex than the brute-force approach.
Requires careful handling of edge cases, like the first digit not being zero and tracking used digits.
Solutions
Solution
class Solution { public int countSpecialNumbers ( int n ) { List < Integer > digits = new ArrayList <>(); while ( n != 0 ) { digits . add ( n % 10 ); n /= 10 ; } int m = digits . size (); int ans = 0 ; for ( int i = 1 ; i < m ; ++ i ) { ans += 9 * A ( 9 , i - 1 ); } boolean [] vis = new boolean [ 10 ]; for ( int i = m - 1 ; i >= 0 ; -- i ) { int v = digits . get ( i ); for ( int j = i == m - 1 ? 1 : 0 ; j < v ; ++ j ) { if ( vis [ j ]) { continue ; } ans += A ( 10 - ( m - i ), i ); } if ( vis [ v ]) { break ; } vis [ v ] = true ; if ( i == 0 ) { ++ ans ; } } return ans ; } private int A ( int m , int n ) { return n == 0 ? 1 : A ( m , n - 1 ) * ( m - n + 1 ); } }Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.