AcWing.338. 计数问题

给定两个整数 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 出现的次数,第二个数字表示 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

 思路:求出每一位数字中0~9出现的次数

假设一个数为七位数 abcdefg ,从中求出1~abcdefg这些数字中1出现的次数,即所有位数上1出现的次数的和

比如求1在第四位上出现的次数:即求有多少如 _ _ _ 1 _ _ _ 这样的数是在范围之内的

        (1)当前三位从001取到abc - 1 ,那么前三位已经决定了这个数一定是小于最大值abcdefg的,那么我们后三位可以随便取得,即从000取到999,那么这种情况有abc * 1k 种选法

        (2)当前三位取abc,那么前三位已经是可取的最大值,那么要进一步进行分类讨论

                1.当第四位d < 1,也就是abcdefg是永远小于abc1_ _ _的,那么这种情况取1的可能为0

                2.当第四位d = 1,也就是这个数为abc1_ _ _,那么前四位已经是可取的最大的,后三位就只能去000~efg,只有这样才能保证不超范围,那么有 efg + 1 种选法

                3.当第四位d > 1,也就是abc1_ _ _前四位并没有取到最大值,那么后三位随便取也可以保证在范围内,即取000 ~ 999,那么有 1k 种情况

最后,比如我们要求数A到数B之间的计数,那么我们只需要用1~B减去1~A,就能得到A~B之间的计数 

注:当需要计数的数字在第一位的时候,就不需要讨论情况(1)了 

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

int get(vector<int> num, int l, int r)	//求i前面这些位数字组成的数字(对应第一种情况)
{
	int res = 0;
	for (int i = l; i >= r; i--) {
		res = res * 10 + num[i];
	}
	return res;
}

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

int count(int n, int x) {	//1到n中统计x的出现次数
	if (!n)return 0;	//如果n等于0直接返回

	vector<int> num;
	while (n) {			//获取n的每一位数
		num.push_back(n % 10);
		n /= 10;
	}

	n = num.size();	//让n等于自己的位数

	int ans = 0;	//ans存答案

	for (int i = n - 1 - !x; i >= 0; i--) {		//i从最高位开始枚举(计算每一位上数字组成情况),且当最高位为0的时候,从第二位开始枚举
		if (i < n - 1) {	//如果枚举到的不是最高位
			ans += get(num, n - 1, i + 1) * power10(i);	//i的前面这些位数字乘上10的i次方(所有的组成方法)
			if (!x) ans -= power10(i);	//如果计算的是0的次数,那么要减去一次10的i次方(从001开始,少了一次10的i次方)
		}

		if (num[i] == x)ans += get(num, i - 1, 0) + 1;	//如果这一位数字等于计算数组,数量为后面这些位组成的数字加上1
		else if (num[i] > x) ans += power10(i);	//如果这一位是大于当前求的数字的,那么就加上10的i次方
	}

	return ans;
}

int main() {
	int a, b;

	while (cin >> a >> b, a || b) {		//a和b不一定谁更小
		if (a > b)swap(a, b);

		for (int i = 0; i < 10; i++)
			cout << count(b, i) - count(a - 1, i) << " ";
		puts("");
	}

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值