Problem
Given a non-negative integer n, count all numbers with unique digits, x, where 0 ≤ x < 10n.
Hint:
A direct way is to use the backtracking approach.
Backtracking should contains three states which are (the current number, number of steps to get that number and a bitmask which represent which number is marked as visited so far in the current number). Start with state (0,0,0) and count all valid number till we reach number of steps equals to 10n.
This problem can also be solved using a dynamic programming approach and some knowledge of combinatorics.
Let f(k) = count of numbers with unique digits with length equals k.
f(1) = 10, …, f(k) = 9 * 9 * 8 * … (9 - k + 2) [The first factor is 9 because a number cannot start with 0].
Example
Given n = 2, return 91. (The answer should be the total numbers in the range of 0 ≤ x < 100, excluding [11,22,33,44,55,66,77,88,99])
Algorithm
整理一下题意:给定一个非负整数n,计算[0,10^n)上的所有的各位不相同的数的数目。例如234、378就是各位不相同的数;22、696、990就不是各位不相同的数。
根据提示,可以通过回溯的方法求解,也可以通过递推公式的方法求解。显然后者会更简单,于是尝试从数学的角度推导递推公式。
设x为[0,10^n)上的任意一个数,count为当[0,10^n)上的所有的各位不相同的数的数目。n=0时,x=0,count=1;当n=1时,x为0,1,2,…,10,count=10。上面两种情况,区间内所有取值都是各位不相同的数。
下面设f(k)为有一个k位数包含各位不相同的数的数目,其中k>=2。对于k位数,
- 左边第1位取值,由于一个两位数最高位不能为0,可取1-9,因此有9种取法;
- 左边第2位取值,可以取0,除去第k-1位所用数字不能取外,还有9种取法;
- 左边第3位取值,同理,有8种取法 ;
- …;
- 左边第k位取值,有9-k+2取法,直到0-9这10种不同的数字都取完。
于是,
f(k)= 9 * 9 * 8 * ... (9 - k + 2) , k>=2
根据以上递推公式,代码如下。
//递推版本,时间复杂度O(n)
class Solution {
public:
int countNumbersWithUniqueDigits(int n) {
if(n==0) return 1;
if(n==1) return 10;
int count=10;
int f=9;
for(int k=2;k<=n;k++){
f*=(9-k+2);
count+=f;
}
return count;
}
};