给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。
示例:
输入: 13
输出: 6
解释: 数字 1 出现在以下数字中: 1, 10, 11, 12, 13 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-digit-one
这题经过lkn大佬的指点,用数位dp的方法还是很简单的,但是需要自己多举几个例子分析一下。
首先 你需要知道 9以内1的个数有1个, 99以内1的个数有20个,999以内1的个数有300个,也就是说 10的n次方以内不包括10的n次方 1的个数有n*(10^(n-1))个
比如 1234 如果我们已经知道了dp[2]=154; 也就是234内1的个数有154个,我们要得到dp[3]的值
dp[3] = 1000以内1的个数(不包括1000) + dp[2] + 235(1xxx都是以1开头,1000~1234有235个数)
在比如 2234
dp[3] = 1000以内1的个数*2(不包括1000) + dp[2] +1000 (1000~1999有1000个数)
由此我们可以得出关系表达式 (x表示当前位上的数字)
dp[i] = 10^(i-1) * i * x + dp[i-1] + 后i位*(x==1) +10^i*(x>1)
代码如下:
int countDigitOne(int n) { if(n<0) return 0; vector<int> dp(100,0); int x=n%10; int N=n; if(x==0)dp[0]=0; else dp[0]=1; n/=10; int i=1; long p=10; while(n){ x=n%10; if(x==0)dp[i]=dp[i-1]; else{ dp[i]=(N%p+1)*(x==1)+dp[i-1]+(i*pow(10,i-1))*x+p*(x>1); } n/=10;i++;p*=10; } return dp[i-1]; }
类似的题目还有求2的个数
面试题 17.06. 2出现的次数
原理相同,稍加修改即可
int numberOf2sInRange(int n) { if(n<1) return 0; vector<int> dp(100,0); int x=n%10; int N=n; if(x<2)dp[0]=0; else dp[0]=1; n/=10; int i=1; long p=10; while(n){ x=n%10; if(x==0)dp[i]=dp[i-1]; else{ dp[i]=(N%p+1)*(x==2)+dp[i-1]+(i*pow(10,i-1))*x+p*(x>2); } n/=10;i++;p*=10; } return dp[i-1]; }
牛客网上有一道通过率为0.00%的题目,我很好奇为啥竟然没有人做出来,然后就作死尝试了一下,因为它就是求n以内1和2的个数和,但是官方给的检测通不过,还说99以内1的个数加2的个数是42,简直了,,,,
题目描述
我寻思不就是把整数用字符串存起来。。。
#include<iostream> #include<stdio.h> #include<cmath> #include<cstring> #include<vector> using namespace std; #define MM 20123 int f1(string n, int k){ int res=0; for(int i=n.length()-k;i<n.length();i++) { res=(res*10+n[i]-'0')%MM; } return res; } int f2(int k){ int res=1; for(int i=0;i<k;i++){ res=res*10%MM; } return res; } int countDigitOneMod(string num) { int n=num.length(); if(num[0]=='-') return 0; vector<int> dp(110,0); char x=num[n-1]; if(x=='0')dp[0]=0; else dp[0]=1; int i=1; while(i<n){ x=num[n-i-1]; if(x=='0')dp[i]=dp[i-1]; else{ int k=f1(num,i); int t=(f2(i-1)*i*(x-'0'))%MM; dp[i]=(k+1)*(x=='1')+dp[i-1]%MM+t+f2(i)*(x>'1'); dp[i]=dp[i]%MM; } i++; } return dp[i-1]; } int countDigitTwoMod(string num) { int n=num.length(); if(num[0]=='-') return 0; vector<int> dp(110,0); char x=num[n-1]; if(x<'2')dp[0]=0; else dp[0]=1; int i=1; while(i<n){ x=num[n-i-1]; if(x=='0')dp[i]=dp[i-1]; else{ int k=f1(num,i); int t=(f2(i-1)*i*(x-'0'))%MM; dp[i]=(k+1)*(x=='2')+dp[i-1]%MM+t+f2(i)*(x>'2'); dp[i]=dp[i]%MM; } i++; } return dp[i-1]; } int main(){ string N; while(cin>>N){ int b=countDigitOneMod(N); int c=countDigitTwoMod(N); cout<<(b+c)%MM<<endl; return 0; }
代码我验证过,反正long那么大的n验算答案是对的,但是牛客网的答案不一样,罢了罢了