leetcode 357. 计算各个位数不同的数字个数

题目描述:

给定一个非负整数 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)。如果要频繁调用这个函数,那么只需要在系统中维护一张表。这应该是此问题的最佳解。

猜你喜欢

转载自blog.csdn.net/liusiweix/article/details/84139793