233. 数字 1 的个数

给定一个整数 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,简直了,,,,

https://www.nowcoder.com/practice/20d6abf0fbcc49a799024e61fa2292c6?tpId=40&tqId=30988&tPage=1&rp=1&ru=/ta/kaoyan&qru=/ta/kaoyan/question-ranking

题目描述

给定正整数N,函数F(N)表示小于等于N的自然数中1和2的个数之和,例如:1,2,3,4,5,6,7,8,9,10序列中1和2的个数之和为3,因此 F(10)=3。输入N,求F(N)的值,1=<N<=10^100(10的100次方)若F(N)很大,则求F(N)mod20123的值

我寻思不就是把整数用字符串存起来。。。

#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验算答案是对的,但是牛客网的答案不一样,罢了罢了

猜你喜欢

转载自www.cnblogs.com/Dancing-Fairy/p/12682635.html
今日推荐