文章目录
- 题目描述
- 问题分析
- 代码
题目描述
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;
}