美文网首页
刷题-leetcode:1015. 至少有 1 位重复的数字

刷题-leetcode:1015. 至少有 1 位重复的数字

作者: marksman_e902 | 来源:发表于2019-03-28 10:31 被阅读0次

    题目地址:https://leetcode-cn.com/problems/numbers-with-repeated-digits/

    题目叙述:

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

    示例 1:

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

    示例 2:

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

    示例 3:

    输入:1000输出:262

    提示:

    1 <= N <= 10^9

    记录一下一波三折的解题过程:

    方法1:

    使用代码最少的方法,两层循环,外层循环从N循环到10,内层循环:N%10,N/=10,从个位往上记录出现的数字,出现重复数字跳出内层循环,count++。

    class Solution {
    public:
        int numDupDigitsAtMostN(int N) {
            int count=0;
        while(N>10){
            int tmpN=N;
            int *recorder=new int[10];
            while(tmpN>=1){
                int tmp=tmpN%10;
                if(recorder[tmp]==1){
                    count++;
                    break;
                }
                recorder[tmp]=1;
                tmpN=tmpN/10;
            }
            N--;
        }
        return count;
        }
    };
    

    时间复杂度评估:O(nlogn),这种方法挑战到第99个测试用例就超时了,进行优化:

    方法2:

    优化了几个地方:1.从高位往个位来查找重复数字,在出现重复的数位将后面部分截断,直接跳过重复数位后面数位的逐个迭代,例如在2020024直接跳到2019999,重复个数加25。2.使用case判断替代pow来计算10的n次方以加快速度。

    class Solution {
    public:
        int numDupDigitsAtMostN(int N) {
            int count=0;
            int x=log10(N);
            int recorder[10];
            int cut;
            int tmpX;
            while(N>10){
                cut=1;
                if(getPower(x)>N){
                    x--;
                }
                tmpX=x;
                memset(recorder, 0, sizeof(int)*10);
                while(tmpX>=0){
                    int power=getPower(tmpX);
                    int tmp=((N/power)%10);
                    if(recorder[tmp]==1){
                        cut=N-((N/power)*power)+1;
                        count+=cut;
                        break;
                    }
                    recorder[tmp]=1;
                    tmpX--;
                }
                N-=cut;
            }
            return count;
        }
        int getPower(int x){
            switch (x){
                case 0:
                    return 1;
                    break;
                case 1:
                    return 10;
                    break;
                case 2:
                    return 100;
                    break;
                case 3:
                    return 1000;
                    break;
                case 4:
                    return 10000;
                    break;
                case 5:
                    return 100000;
                    break;
                case 6:
                    return 1000000;
                    break;
                case 7:
                    return 10000000;
                    break;
                case 8:
                    return 100000000;
                    break;
                case 9:
                    return 1000000000;
                    break;
                default:
                    return 1;
            }
        }
    };
    

    时间复杂度评估:O(nlogn),这种方法坚持到第110个测试用例才超时,看来得换个思路了。

    方法3:

    认真分析其实这是一道高中的排列组合题目,计算出不重复的数字的个数,然后用N来减就行了。N-1数位的数字的组合其实可以很方便地计算出来:9x9x8x...(最大数位k-1个数相乘)+9x9x8x...(最大数位k-2个数相乘)+...+9即可算出。麻烦的是N数位的数字的组合难以计算,从高数位往低数位迭代,分为三步:

    1.最大数位:最大位数字n进行-1然后乘以9x8x...(最大数位k-1次),将结果累加;

    2.中间数位J:判断前面是否有重复数字,有的话剩下的事情就别干了,break循环,没有的话:计算前面小于当前数位数字的数字的个数z,当前位数数字减去这个个数z得到v,vx((9-j)x(8-j)x...(最大数位k-j-1次乘法迭代)),将结果累加,J往后递增;

    3.末尾数:判断前面是否有重复数字,有的话剩下的事情就别干了,没有的话:计算前面小于当前数位数字的数字的个数z,当前位数数字减去这个个数z得到v,将v累加;

    最后返回N-累加结果;

    class Solution {
    public:
        int numDupDigitsAtMostN(int N) {
            if(N<=10)
                return 0;
            int rawN=N;
            int count=0;
            int x=log10(N);
            int *n = new int[x+1];
            for(int i=x;i>=0;i--){
                n[i]=N%10;
                N/=10;
            }
            count+=getFloor2(x-1);
            int duplicated=0;
            for(int j=0;j<=x&&duplicated==0;j++){
                if(j==0){
                    count+=(n[j]-1)*getFloor3(x-1,0);
                }
                else if(j==x){
                    int tmp=n[j]+1;
                    int k[10] = {};
                    for(int v=0;v<j;v++){
                        if(k[n[v]]==1){
                            duplicated=1;
                            break;
                        }
                        if(n[j]>=n[v]){
                            tmp--;
                        }
                        k[n[v]]=1;
                    }
                    if(duplicated==0)
                        count+=tmp;
                }
                else{
                    int tmp=n[j];
                    int k[10] = {};
                    for(int v=0;v<j;v++){
                        if(k[n[v]]==1){
                            duplicated=1;
                            break;
                        }
                        if(n[j]-1>=n[v]){
                            tmp--;
                        }
                        k[n[v]]=1;
                    }
                    if(duplicated==0)
                        count+=tmp*getFloor3(x-j-1,j);
                }
            }
            return rawN-count;
        }
        int getFloor(int n){
            int res=1;
            for(int i=9,j=0;j<=n;j++){
                res*=i;
                if(j!=0){
                    i--;
                }
            }
            return res;
        }
        int getFloor2(int x){
            if(x<0)
                return 0;
            int res=0;
            for(int i=x;i>=0;i--){
                res+=getFloor(i);
            }
            return res;
        }
        int getFloor3(int n,int o){
            if(n<0)
                return 0;
            int res=1;
            for(int i=9-o,j=0;j<=n;j++,i--){
                res*=i;
            }
            return res;
        }
    };
    

    方法3完美运行。时间复杂度评估:O(logn)。

    image

    其他:

    看到其他用户的解答用到一种叫做dp数位的方法。dp数位是一个类似动态规划(一个问题可以化为同样类型的几个子问题)的递归方法,适用于类似需要在一个区间段寻找符合条件的数字个数的题目,有时间学习一下。

    相关文章

      网友评论

          本文标题:刷题-leetcode:1015. 至少有 1 位重复的数字

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