LeetCode 1012. Numbers With Repeated Digits

题目描述

        给定正整数 N,返回小于等于 N 且具有至少 1 位重复数字的正整数。

样例

输入:20
输出:1
解释:具有至少 1 位重复数字的正数(<= 20)只有 11 。

输入:100
输出:10
解释:具有至少 1 位重复数字的正数(<= 100)有 11,22,33,44,55,66,77,88,99 和 100。

输入:1000
输出:262

注意

1 <= N <= 10^9

算法

(数位统计) O((logn)^2)
1、题目要求的是至少 1 位重复正整数的个数,难以统计,不妨统计所有数位都不重复的正整数的个数。

2、首先统计数位个数少于 N 的数字个数,运用排列计数规则很容易计算出来。假设数位为 1 位,则个数
为 9 个;假设数位为 L (> 1) 位,则个数为 9∗9∗8∗7∗⋯∗(9−L+2)9∗9∗8∗7∗⋯∗(9−L+2)。

3、然后考虑数位个数等于 N 的数字个数,这里我们从高位到低位统计。假定当前位填不超过 N 当前位的
数字,然后同样运用以上规则,统计个数;然后再将这一位固定为 N 当前位的数字,继续向低位统计。统计
过程中,如果出现了重复用的数字,即 N 本身就有重复数字,则直接返回 N - ans 即可。最后,返回 
N - ans - 1 这里减的 1 就是 N 本身。

4、以数字 34 为例,首先统计出数位为 1 位的个数,显然为 9 个。然后尝试最高位填 1 和 2,填好之
后,第二位都有 9 种填法,故这样有 18 种。然后固定最高位为 3,填下一位。下一位可以填 0, 1, 2,
共有 3 种,所以答案为 34 - 9 - 18 - 3 - 1 = 3。

 

C++ 代码

class Solution {
public:
    int numDupDigitsAtMostN(int N) {
        string n(to_string(N));
        reverse(n.begin(), n.end());
        int ans = 0;
        for (int len = 1; len <= n.length() - 1; len++) {
            int tmp = 9;
            for (int j = 9, k = len - 1; k >= 1; k--, j--)
                tmp *= j;
            ans += tmp;
        }

        vector<bool> used(10, false);

        for (int i = n.length() - 1; i >= 0; i--) {
            int tmp = 0;
            for (int j = 0; j < n[i] - '0'; j++)
                if (!used[j])
                    tmp++;
            if (i == n.length() - 1)
                tmp--;

            if (tmp > 0) {
                for (int j = 10 - (n.length() - i), k = i - 1; k >= 0; k--, j--)
                    tmp *= j;
                ans += tmp;
            }
            if (used[n[i] - '0'])
                return N - ans;
            used[n[i] - '0'] = true;
        }
        return N - ans - 1;
    }
};
发布了31 篇原创文章 · 获赞 38 · 访问量 5030

猜你喜欢

转载自blog.csdn.net/qq_41685265/article/details/104232824