至少有一位重复数字--动态规划

leetcode 1012

至少有一位重复的数字

题目描述:给定正整数 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

思路:题目要我们找出有重复数字的数字个数,反向思维,我们只要找出所有没有重复数字的数字个数total,再用N-total,就能得到结果

现在问题就是如何才能找到没有重复数字的数字个数

我们可以通过高低位重新组合得到,举个例子

现在要求222的不重复数字个数

  1. 先去掉首位

     step    	1th 	2th 	3th 	total
     1       	0   	0   	1-9 	9*A(9,0)
     2       	0   	1-9		0-9 	9*A(9,1)
    

    我们解释一下这个第一步,我们把A里边的0理解为坑位,因为前面两个坑位已经被占用了,即0,因此只剩下一个坑位,而为了保证它不为0,我们用9来乘于它,然后将这个坑位让给9,也就是1-9,因此坑位就是0了,第二步同理,先让出一个坑位给9,保证首位不为0

    代码实现

     for(i=j-1; i>=1; i--)
     total += 9*A(9,i-1);
    
  2. 考虑首位的情况

     step    1th 	2th 	3th 	total
     1       0-1 	0-9 	0-9 	1*A(10-1,2)
     2       2   	0-1 	0-9 	2*A(10-2,1)
     3       2   	2   	0-1 	2*A(10-3,0)
    

    我们分析一下第一步,A里边的10-1表示一个数字已经被固定好了,2表示有两个坑位,我们对这两个坑位进行分配就好了

    对比一下第一步,我们看第二步,第二步是先固定第一位,然后第二位在一个范围变化,0-1就是2,A里边的10-2表示两个已经固定住了,1表示还要一个坑位等待分配,但是同样是0-1,在第二步里边是2,在第一步里边确实1,为啥?因为第一步是首位,首位不能为0,因此我们将第一步的乘积去掉1个即1*A(10-1,2)

    代码实现

     for(i=j-1; i>=0; i--)
     {
         int num = dig[i];
         for(int k=(i==j-1?1:0); k<num; k++)
         {
             if(used[k] != 0)
                 continue;
             total += A(10-(j-i),i);
         }
         used[num]++;
         if(used[num]>1)
             break;
         if(i==0)
             total++;
     }
    

从上面两步我们基本知道了思路,但是有没有发现。

第二步里边其实是包含着一些带重复数的情况的,我们看一下第二步中的step3,前面两位就是2了,因此我们还需要将重复的给剔除掉,现在问题是如何剔除这些重复数

我们维护一个数组used[10],用它来保存一个数是否被用过,如何理解被用过?,当前面有一个位的数字被使用过,我们在后边应当不再使用它,我们看一下具体代码

for(int k=(i==j-1?1:0); k<num; k++)
{
    if(used[k] != 0)
        continue;
    total += A(10-(j-i),i);
}

这段代码就类似于前面第二步中step2中的2*A(10-2,1),2就是0-1这两个数,假如一个和前面相同,我们就不用它了,因此我们不使用直接乘的方法,用累加的方法,才能剔除掉重复的。

used[num]++;
if(used[num]>1)
    break;
if(i==0)
    total++;

如何理解这段代码,

  • 按我们的思路,这一位数字现在是变化的,在下一步我们就会将其固定住,因此先将其used++,反正后边的数字和它相同,而i==0, total++,就表示原数即N是一个无重复数字的数,你看一下完整代码肯定能理解

完整代码

#define M 10

int A(int i, int j)
{
    int n, m, sum;
    m=j;
    sum=1;
    for(n=i; m>0; i--,m--)
        sum*=i;

    return sum;
}
int numDupDigitsAtMostN(int N){
    if(N<=10)
        return 0;
    int i, j, dig[M];
    int total=0, flag=0;
    int used[10];
    i = N;
    j = 0;
    while(i)
    {
        dig[j]=i%10;
        j++;
        i=i/10;
    }
    for(i=0; i<10; i++)
        used[i]=0;
    for(i=j-1; i>=1; i--)
        total += 9*A(9,i-1);
    for(i=j-1; i>=0; i--)
    {
        int num = dig[i];
        for(int k=(i==j-1?1:0); k<num; k++)
        {
            if(used[k] != 0)
                continue;
            total += A(10-(j-i),i);
        }
        used[num]++;
        if(used[num]>1)
            break;
        if(i==0)
            total++;
    }
    return N-total;
}

题目来源以及解法参考:

作者:LeetCode-Solution

链接:https://leetcode-cn.com/problems/count-square-submatrices-with-all-ones/solution/tong-ji-quan-wei-1-de-zheng-fang-xing-zi-ju-zhen-2/

来源:力扣(LeetCode)

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处

猜你喜欢

转载自blog.csdn.net/jump_into_zehe/article/details/106305531
今日推荐