题目描述:
给定一个非负整数 n,计算各位数字都不同的数字 x 的个数,其中 0 ≤ x < 10^n 。
示例:
输入: 2
输出: 91
解释: 答案应为除去 11,22,33,44,55,66,77,88,99
外,在 [0,100) 区间内的所有数字。
思路:显然这是一个排列问题。显然,对长度为n(2<=n<=10)的数,不重复的数字个数为:C(9,1)*A(9,n-1)。解释:先排最高位,最高位不能是0,只有9种取值可能;后面的n-1位,由于最高位取走了一个数,所以也是9个数里取n-1个数的排列。对于n>10,每个数中必定出现重复数字;对于n=1,应该是0~9,共十个数字。n=0,没有这样的数字。
假定所求的是f(n),f(n)的含义是:长度在闭区间[0,n]中的,没有重复数字的数的个数。根据上面的推导:
f(0)=1,
f(1)=10,
当2<=n<=10时 f(n)= 9*A(9, n-1)+f(n-1),
当n>10时,f(n) = f(10)。
int countNumbersWithUniqueDigits(int n) {
if(n==0) return 1;
if(n==1) return 10;
int m= n>10 ? 10: n;
//初值:f(1)
int ans=10;
//f(n)=f(n-1)+9*A(9,n-1);
int A=9;
//用A保存9*A(9,n-1)的中间量,避免重复计算
for(int i=9;i>10-m;i--)
{
A=A*i;
ans=ans+A;
}
return ans;
}
算法的时间复杂度为O(n),空间复杂度为O(1)
看到另一个有趣的思路:
int countNumbersWithUniqueDigits(int n) {
int ret[] = {1, 10, 91, 739, 5275, 32491, 168571, 712891, 2345851, 5611771, 8877691};
return ret[n > 10 ? 10 : n];
}
时间空间复杂度都是O(1)。如果要频繁调用这个函数,那么只需要在系统中维护一张表。这应该是此问题的最佳解。