概述
题目链接: https://leetcode-cn.com/problems/numbers-with-repeated-digits/
题目内容:给定正整数 N
,返回小于等于 N
且具有至少 1 位重复数字的正整数。
思路:
看到题目,求范围内出现某类数字的个数,思路应该是数位dp的方向。
关于数位dp是什么,这篇博客十分清楚地介绍了:https://blog.csdn.net/wust_zzwh/article/details/52100392
题目要求得出范围内至少有1位重复的个数,采用逆向的想法就是 先求出 范围内每位的数字相互不重复的个数 ss,用总数N - ss(总数减掉每位数字不重复的情况) 就可以得到我们的结果;
①状态dp[i][j]: i表示当枚举到数字的 第 i 位,j表示在 第i位之前出现过的所有数字(比如前面出现过7、8、 9三个数字) ,
dp[i][j] 表示枚举到第 i 位,前面状态为 j 时,没有出现重复数字的数字个数。
要用1个数字 j 表示前面出现过7,8,9这三个数字,这里用到了状态压缩,只是简单应用,比如在第i位之前 前面出现过 7 8 9 ,
那么 j 就是 (1<<7) | (1<<8) | (1<<9) = 128 + 256 + 512 = 896 , 这样我们用一个数字就可以表示前面 出现过的数字。
②状态转移: 如果当前第 i位 的数字是x[i],if((x[i] & j) != 0) {就说明了前面出现过了这个数字x[i],不进行递归},else {否则 递归 查找下一位 }。
③代码:
class Solution {
int pow[] = new int []{ //pow[i] 表示 (1<<i) 的结果
1,2,4,8,16,32,64,128,256,512
};
int x[] = new int[20+1];
int dp[][] ; // dp[i][j] 表示枚举到第i位,前面的状态是j,没有出现过重复数字的数目
int dfs(int pos,int pre,boolean limit,boolean flag)
//枚举到pos位,前面的状态是pre,limit表示是不是上限,flag表示是否前缀0
{
if(pos == -1) {
if(flag){//全是0,直接返回0
return 0;
}
return 1;//前面不全是0,走到了最后,说明每一位数字都不同,返回1
}
if(!limit && dp[pos][pre]!=-1) return dp[pos][pre];
int up = limit?x[pos]:9;
int rs = 0;
for(int i=0;i<=up;i++)
{
if(!flag && (pre & pow[i])!=0){ //不是前缀零,那么出现重复就不往后面走
continue;
}
int tmp = 0;//因为前缀0所以判断,1如果不是前缀0,叠加状态,2如果前缀是0,当前位也是0,直接传递 0 状态,最后返回dp[0][0].
if(!(flag && i==0)){
tmp = pre | pow[i];
}
//递归下一位数字
rs = rs + dfs(pos-1,tmp,limit&&i==up,flag && i==0);
}
if(!limit){
dp[pos][pre] = rs;
}
return rs;
}
int solve(int n)
{
int pos = 0;
while(n>0)
{
x[pos++] = n%10;
n = n/10;
}
return dfs(pos-1,0,true,true);
}
public int numDupDigitsAtMostN(int N) {
int sum = 0;
for(int i=0;i<pow.length;i++)
{
sum = sum + pow[i];
}
dp = new int [10+1][sum+1];
//初始化数组
for(int i=0;i<dp.length;i++)
for(int j=0;j<dp[i].length;j++)
dp[i][j] = -1;
//最原始的,只有1位数字的情况, 1~9一共是9种。
dp[0][0] = 9;
return N-solve(N);//总数 - 没有出现重复数字的个数 = 出现重复位的个数。
}
}
最后
以上就是重要鸡翅为你收集整理的leetcode1015 至少有1位重复的数字的全部内容,希望文章能够帮你解决leetcode1015 至少有1位重复的数字所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复