Loading

计数问题

题目链接:https://www.acwing.com/problem/content/340/

题目描述

给定两个整数 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

分析

本题虽然顶着数位DP的标签,但我目前实在是不明白跟DP啥关系,以后了解更多了再来更新。

问题转换

我们要求给定x,y闭区间上所有数字上,0,1,2,···,9出现的次数。
我们可以将问题转换,运用类似前缀和的思想来思考。
count(i,j)表示在[1,i]这个闭区间上所有的数字中j出现的次数,如果我们可以求解这个问题,那我们就可以解决本题。

核心思路

对于这类问题,核心思路就是分类讨论
对于给定的一个数 m=abcdefg 作为上界,以求解n(1 <= n <= 9)在第四位的出现次数为例进行分析。
第四位位y的数形如: xxxdyyy
当d!=0时:

  1. xxx -> [000,abc-1]
    yyy -> [000,999] 共abc*1000次
  2. xxx=abc
    d=n yyy -> [000,efg] 则共efg+1次
    d<n yyy -> [000,999] 则共1000次
    d>n 显然共0次

当d=0时:

  1. xxx -> [001,abc-1]
    yyyy -> [000,999] 共(abc-1)*1000次
    因为 000 0 yyy 这个数实际上不存在第4位,所以000开头不能被计入。
  2. xxx=abc
    d=n yyy -> [000,efg] 则共efg+1次
    d<n yyy -> [000,999] 则共1000次

以上就是对d位于中间数的情况的讨论,按照同样的方式就可以对d在最高位或最低位的情况进行讨论,从而高效解决计数。

AC代码


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

using namespace std;

int get(vector<int> num, int l, int r)
{
	int res = 0;
	for (int i = r; i >= l; i--)
	{
		res = res * 10 + num[i];
	}
	return res;
}

int count(int x, int n)
{
	//如果传入的x等于0 返回的res为0
	vector<int> num;
	while (x)
	{
		num.push_back(x % 10);
		x /= 10;
	}
	int res = 0;
	for (int i = 0; i < num.size(); i++)
	{
		if (i == 0) //对最低位的处理
		{
			res += get(num, i + 1, num.size() - 1);
			if (n == 0)
				res--;
			if (n <= num[i])
				res++;
			continue;
		}
		if (i == num.size() - 1) //对最高位的处理
		{
			if (n == 0) //最高位是0直接pass
				continue;
			if (n < num[i])
				res += (int)pow(10, num.size() - 1);
			if (n == num[i])
				res += get(num, 0, i - 1) + 1;
			continue;
		}

		//对中间位的处理
		res += get(num, i + 1, num.size() - 1) * (int)pow(10, i);
		if (n == 0)
			res -= (int)pow(10, i);
		if (n < num[i])
			res += (int)pow(10, i);
		if (n == num[i])
			res += get(num, 0, i - 1) + 1;
	}
	return res;
}

int main()
{
	int a, b;
	while (cin >> a >> b, a || b)
	{
		if (a > b)
			swap(a, b);
		for (int i = 0; i < 10; i++)
		{
			if (i)
				cout << " " << count(b, i) - count(a - 1, i);
			else
				cout << count(b, i) - count(a - 1, i);
		}
		cout << endl;
	}
	return 0;
}
posted @ 2022-12-07 17:25  Kong-Aobo  阅读(25)  评论(0编辑  收藏  举报