AcWing 338. 计数问题

题目描述

AcWing 338.计数问题
给定两个整数 a a a b b b, 求 a a a b b b中所有数字中0~9的出现次数
数据范围:
0 < a, b < 100000000

输入格式

输入包含多组测试数据。

每组测试数据占一行,包含两个整数 a
和 b。
当读入一行为0 0 时表示终止,且此行不做处理
输入样例

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

问题分析

这道题让我们求一个区间的数出现的次数,那么我们可以利用前缀和的思想求得从 1 ~ n 的 某个数x的出现次数,只有用区间末端点减区间的前端点的前一个,就可以得到一个区间某个数出现的次数设计一个cout(n, x) 函数。

那我们要怎么求这个范围数的出现次数呢,可能你会想到暴力枚举,从1 ~ n一次枚举x出现了几次,最后将答案累加返回,其实不用这样。
我们只需要知道这个区间的最大值记作abcdefg,这样我们分析这一个数中x出现的位置从而找到在这个位置的所有情况即可
但是有个特殊情况当x = 0的情况下:
这时只会影响当前三位为000~abc的情况,由于要统计0的个数所以如果放任前导零的存在就会影响答案的结果,而x为其他情况即使有前导零对结果也不会造成影响
这时就要满足当求第k位为0是要保证前面不能全为0,所以000~abc 中会少000这一种情况变成001 ~ abc这里虽然有前导零但是这些前导零是已经在 k ′ k^{'} k小于k时就求过了的这里只是一种表现形式而已不会对答案造成影响

代码

在这道一种由于要知道一个数第几位前面的数,这位数和右边的数所以要熟练掌握/ %来进行

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

int split(int x, int k)
{
    int p = pow(10, k - 1);
    printf("左边的数:%d\n", x / p / 10);
    printf("从右往左第k位的数:%d\n", x / p % 10);
    printf("右边的数:%d\n", x % p);
}

int main()
{
    int x, k;
    scanf("%d%d", &x, &k);
    split(x, k);
    return 0;
}

执行结果

输入:123456789   3
输出:
左边的数:123456
第k位的数:7
右边的数:89


输入:123456789   4
输出:
左边的数:12345
从右往左第k位的数:6
右边的数:789

实现

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 1e8 + 10;
int a, b;

int dgt(int x)//计算x这个数有多少位
{
    int res = 0;
    while (x) res ++, x /= 10;
    return res;
}

int cnt(int i, int x)
{
    int res = 0, n = dgt(i);
    for (int j = 1; j <= n; j ++) // 枚举j在i的第几位从后向前枚举
    {
        // p表示j后面后面有多少数
        int p = pow(10, j - 1), dl = i / p / 10, dj = i / p % 10, dr = i % p;
        
        //当第j位要查0的情况下,即j这里就是0了,那么就不允许前面都是0,这是非法的所以要剔除
        if (!(x || dl)) break;
        // p表示从后往前第j个位置有多少位10
        // dl是前面的数 dj是第j个位置的数 dr是后面的数
        if (x == 0 && dl) res += (dl - 1) * p;// 当要查的是0的时候就不能允许前导0的存在去掉000的情况
        if (x) res += dl * p;// 这就是当前面是000 ~ abc - 1的情况
        
        //下面就是当前面是abc时此时可能是最外层会被限制
        if ((dj == x)) res += dr + 1;
        if ((dj > x)) res += p;// 没有限制000 ~ 999
    }
    return res;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ˇasushiro

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

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

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

打赏作者

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

抵扣说明:

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

余额充值