美文网首页
1015. Numbers With Repeated Digi

1015. Numbers With Repeated Digi

作者: stationaryi | 来源:发表于2019-03-22 13:54 被阅读0次

    Given a positive integer N, return the number of positive integers less than or equal to N that have at least 1 repeated digit.

    Example 1:

    Input: 20
    Output: 1
    Explanation: The only positive number (<= 20) with at least 1 repeated digit is 11.
    Example 2:

    Input: 100
    Output: 10
    Explanation: The positive numbers (<= 100) with atleast 1 repeated digit are 11, 22, 33, 44, 55, 66, 77, 88, 99, and 100.
    Example 3:

    Input: 1000
    Output: 262

    Note:

    1 <= N <= 10^9

    Note:

    这题的题意是给一个整数,找到小于或者等于这个数且有至少一个重复数的数字的个数。这题刚开始做的时候完全没有思路,想着枚举出全部数字,发现是个死胡同,于是就看答案啦~

    步骤很简单,反过来考虑就可以了。
    如果想找出小于等于某个数且至少有一个重复数字的个数,先计算出没有重复数字的个数,再用当前数去剪就可以了。

    计算没有重复数字个数的步骤,我们以 7654 为例。
    1.先计算百位以及百位以下的数。
    我们有
    XXX ---- 9 * 9 * 8
    XX--------9 * 9
    X----------9

    总共有 9 * 9 * 8 + 9 * 9 + 9 = 738

    2.计算包含前缀的没有重复的数字
    XXXX --- 6 * 9 * 8 * 7 (第1位小于7, 不包括0,有6个数)
    7XXX --- 6 * 8 * 7 (第2位小于6, 包括0, 有6个数)
    76XX --- 5 * 7 (第3位小于5, 包括0, 有5个数)
    765X --- 5 (第4位小于等于4, 包括0, 有5个数)

    结果为7654 - 4138 = 3516

    这题的corner cases 挺有意思的, 我们先上代码。

        public int numDupDigitsAtMostN(int N) {
            List<Integer> digits = new ArrayList<>();
        //这个N + 1是解法的精髓,想想如果N=20 (末尾为0的情况)
            for (int tmp = N + 1; tmp > 0; tmp /= 10) {
                digits.add(0, tmp % 10);
            }
    
            int len = digits.size();
    
            int noRepeatNum = 0;
            for (int i = 0; i < len - 1; i++) {
                noRepeatNum += calNoRepeatWithinDigitNum(i);
            }
            // 用set来记录经过的prefix
            // 如果出现N=220, 当我们发现十位上的数已经出现过了
            // break掉loop,也就是说不用计算22X
            Set<Integer> set = new HashSet<>();
            for (int i = 0; i < len; i++) {
                int start = 1;
                if (i != 0) {
                    start = 0;
                }
                for (int j = start; j < digits.get(i); j++) {
                    if (!set.contains(j)) {
                        noRepeatNum += calNoRepeatWithinDigitNum(9 - i, len - i - 1);
                    }
                }
                if (set.contains(digits.get(i))) break;
    
                set.add(digits.get(i));
            }
            return N - noRepeatNum;
        }
    
        private int calNoRepeatWithinDigitNum(int num) {
            int res = 9;
            for (int i = 0; i < num; i++) {
                res *= (9 - i);
            }
            return res;
        }
    
        private int calNoRepeatWithinDigitNum(int largestNum, int num) {
            if (num == 0) return 1;
            int res = largestNum;
            for (int i = 1; i < num; i++) {
                res *= (largestNum - i);
            }
            return res;
        }
    

    corner case1. N = 20 (N 以0 结尾)
    把N+1当做input, 理解了代码就会发现,我们不用把最后一位单独处理了,不然的的话最后一位就要翻译0和非0两种情况讨论,非常麻烦

    corner case2. N = 220 (N 里面有重复的数字)
    如果有重复的数字,break掉整个loop

    相关文章

      网友评论

          本文标题:1015. Numbers With Repeated Digi

          本文链接:https://www.haomeiwen.com/subject/pevlvqtx.html