357. Count Numbers with Unique Digits

题目:

Given a non-negative integer n, count all numbers with unique digits, x, where 0 ≤ x < 10n.

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])

Hint:

  1. A direct way is to use the backtracking approach.
  2. 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.
  3. This problem can also be solved using a dynamic programming approach and some knowledge of combinatorics.
  4. Let f(k) = count of numbers with unique digits with length equals k.
  5. f(1) = 10, ..., f(k) = 9 * 9 * 8 * ... (9 - k + 2) [The first factor is 9 because a number cannot start with 0].

题意:

给定一个非负整数n,计算所有各位数字均不同的数字x的个数,其中0 ≤ x < 10^n。

Hint:

1、一个直接的方法是使用回溯法;

2、回溯应当包含三个状态(当前数字,得到该数字需要的步数,以及一个比特掩码用来表示当前已经访问过哪些位)。从状态(0,0,0)开始,并计算直到10^n为止的有效数字个数。

3、这个问题也可以使用动态规划方法与一些组合的知识来求解。

4、f(k) = 长度为k的不重复数位的数字个数。

5、f(1) = 10, ..., f(k) = 9 * 9 * 8 * ... (9 - k + 2) [首个因数是9,因为数字不能从0开始].


思路一:

由提示5可得,一位数满足题意的数字个数为10个(0到9),两位数满足要求的数字个数为81个,[10到99]这90个数中去掉[11,22,33,44,55,66,77,88,99]这9个数字,还剩91个。通项公式为f(k) = 9 * 9 * 8 * ... (9 - k + 2) 。

代码:C++版:0ms

class Solution {
public:
    int countNumbersWithUniqueDigits(int n) {
        if (n == 0) return 1;
        int res = 0;
        for (int i=1; i<=n; ++i) {  //累加每一位包含的数字个数
            res += count(i);
        }
        return res;
    }
    int count(int k) { //计算f(k) = 9 * 9 * 8 * ... (9 - k + 2)
        if (k < 1) return 0;
        if (k == 1) return 10;
        int res = 1;
        for (int i=9; i>=(11-k); --i) { 
            res *= i;
        }
        return res * 9;
    }
};

思路二:

思路与思路一一致,只是将以上两步代码精简到一个for循环中实现。

代码:C++版:0ms

class Solution {
public:
    int countNumbersWithUniqueDigits(int n) {
        if (n == 0) return 1;
        int res = 10, cnt = 9;
        for (int i=2; i<=n; ++i) {
            cnt *= (11 - i);  //计算f(k) = 9 * 9 * 8 * ... (9 - k + 2)
            res += cnt; //将每一个数位长度的数字个数累加起来
        }
        return res;
    }
};

思路三:

回溯法实现。维护一个变量used,其二进制第i位为1表示数字i出现过,刚开始我们遍历1到9,对于每个遍历到的数字,现在used中标记已经出现过,然后在调用递归函数。在递归函数中,如果这个数字小于最大值,则结果res自增1,否则返回res。然后遍历0到9,如果当前数字没有在used中出现过,此时在used中标记,然后给当前数字乘以10加上i,再继续调用递归函数,实现遍历所有的情况。

代码:C++版:216ms

class Solution {
public:
    int countNumbersWithUniqueDigits(int n) {
        int res = 1, max = pow(10, n), used = 0;
        for (int i=1; i<10; ++i) {
            used |= (1<<i);
            res += search(i, max, used);
            used &= ~(1 << i);
        }
        return res;
    }
    int search(int pre, int max, int used) {  
        int res = 0;
        if (pre < max) ++res;
        else return res;
        for (int i=0; i<10; ++i) {
            if (!(used & (1 << i))) {
                used |= (1<<i);
                int cur = 10 * pre + i;
                res += search(cur, max, used);
                used &= ~(1 << i);
            }
        }
        return res;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值