数位统计DP——计数问题(acwing338)

本文介绍了一个编程问题,要求计算两个整数a和b之间所有数字中0到9的出现次数。给出了详细的解答过程,包括分类讨论和C++代码实现,展示了如何使用位操作来解决这个问题。
摘要由CSDN通过智能技术生成

一、题目

给定两个整数 a 和 b,求 a 和 b 之间的所有数字中 0∼9的出现次数。

例如,a=1024,b=1032,则 a 和 b 之间共有 9个数如下:
1024 1025 1026 1027 1028 1029 1030 1031 1032

其中 0 出现 10次,1 出现 10 次,2 出现 7 次,3 出现 3次等等…

输入格式
输入包含多组测试数据。
每组测试数据占一行,包含两个整数 a和 b。
当读入一行为 0 0 时,表示输入终止,且该行不作处理。

输出格式
每组数据输出一个结果,每个结果占一行。
每个结果包含十个用空格隔开的数字,第一个数字表示 0 出现的次数,第二个数字表示 1 出现的次数,以此类推。

数据范围
0<a,b<100000000

输入样例:
1 10
44 497
346 542
1199 1748
1496 1403
1004 503
1714 190
1317 854
1976 494
1001 1960
0 0

输出样例:
1 2 1 1 1 1 1 1 1 1
85 185 185 185 190 96 96 96 95 93
40 40 40 93 136 82 40 40 40 40
115 666 215 215 214 205 205 154 105 106
16 113 19 20 114 20 20 19 19 16
107 105 100 101 101 197 200 200 200 200
413 1133 503 503 503 502 502 417 402 412
196 512 186 104 87 93 97 97 142 196
398 1375 398 398 405 499 499 495 488 471
294 1256 296 296 296 296 287 286 286 247

二、解答

若有整数abcdefg,现在要找0-abcdefg中m的个数,可以分别找第i位为m的个数,即形如xxxmxxx的数的个数。,最后再相加。

这里假设g为第0位,f为第1位…

eg:

首先用一个例子,即找第[3]位即d所在位置为x的个数(其它位的将[]中的数字替换)

分情况讨论,分为4类:(实际只用讨论3类)

第一类: 最高位小于abc,即从0到abc-1
则最高三位有abc种可能,后三位(即efg所在)有999-000+1=1000种可能,即10的[3]次方种可能

第二类:最高位等于abc,且d>x
则后三位可以从000取到999,即10的[3]次方种可能

第三类: 最高位为abc,且d=x
则后三位只能从000取到efg,则有efg+1种可能

第四类: 最高位为abc,且d<x
有0种可能(所以这一类不用讨论)

!!!注意:
当x为0,即要找0的个数时,需要考虑到0的前面不能都是0,这样的数是不合法的(如要找第3位是0的数,则该数不能为0000123,而只能是类似10123,即0的前面至少要有一个非0的数)

所以如果x=0,则最高位不能为000,只能从001开始,取到abc,共有abc-1个

三、代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;

//从容器a中获取l到r的数,如从12345获取0-2的数为123
int get(vector<int> a,int l,int r){
    int res=0;
    for(int i=r;i>=l;i--){
        res=res*10+a[i];
    }
    return res;
}

//求10的a次方
int power10(int a){
    int res=1;
    while(a--) res*=10;
    return res;
}

//输出0-a的数中x的个数
int cnt(int a,int x){
    if(!a) return 0;//特判,如果a=0,则直接返回0
    int size=0;
    vector<int> v;
    while(a){
        v.push_back(a%10);
        size++;
        a/=10;
    }
    int sum=0;
    //求x在第i个位置的个数
    for(int i=0;i<size-!x;i++){
        sum+=get(v,i+1,size-1)*power10(i);
        if(i<size-1&&!x) sum-=power10(i);//如果是0,则高位不能都是0,要从001开始计算,故上一步的get值要减一
        if(v[i]==x) sum+=get(v,0,i-1)+1;
        else if(v[i]>x) sum+=power10(i);
    }
    return sum;
}

int main(){
    int a,b;
    cin>>a>>b;
    while(!(a==0&&b==0)){
        if(a<b) swap(a,b);
        for(int i=0;i<=9;i++){
            cout<<cnt(a,i)-cnt(b-1,i)<<" ";
        }
        cout<<endl;
        cin>>a>>b;
    }
    
    return 0;
}
  • 22
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云霄星乖乖的果冻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值