zoukankan      html  css  js  c++  java
  • [LeetCode] 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].

    Credits:
    Special thanks to @memoryless for adding this problem and creating all test cases.

    这道题让我们找一个范围内的各位上不相同的数字,比如123就是各位不相同的数字,而11,121,222就不是这样的数字。那么我们根据提示中的最后一条可以知道,一位数的满足要求的数字是10个(0到9),二位数的满足题意的是81个,[10 - 99]这90个数字中去掉[11,22,33,44,55,66,77,88,99]这9个数字,还剩81个。通项公式为f(k) = 9 * 9 * 8 * ... (9 - k + 2),那么我们就可以根据n的大小,把[1, n]区间位数通过通项公式算出来累加起来即可,参见代码如下:

    解法一:

    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) {
            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;
        }
    };

    下面这种方法是上面方法的精简版,思路完全一样:

    解法二:

    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);
                res += cnt;
            }
            return res;
        }
    };

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

    解法三:

    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;
        }
    };

    参考资料:

    https://leetcode.com/discuss/107981/backtracking-solution

    https://leetcode.com/discuss/108119/java-concise-dp-solution

    LeetCode All in One 题目讲解汇总(持续更新中...)

  • 相关阅读:
    剑指offer-面试题11-旋转数组的最小数字-二分法
    剑指offer-基础练习-快速排序-排序
    剑指offer-面试题10-斐波那契数列-递归循环
    剑指offer-面试题9-用两个栈实现队列-栈和队列
    剑指offer-面试题8-二叉树的下一个节点-二叉树
    剑指offer-面试题7-重建二叉树-二叉树
    Android手势识别总结
    Android点击Button按钮的四种事件监听方法总结
    Android点击EditText文本框之外任何地方隐藏键盘的解决办法
    spring boot 热部署
  • 原文地址:https://www.cnblogs.com/grandyang/p/5582633.html
Copyright © 2011-2022 走看看