题目地址:
https://www.acwing.com/problem/content/340/
给定两个正整数 a a a和 b b b,求 [ a , b ] [a,b] [a,b]之间所有数字中 0 ∼ 9 0\sim 9 0∼9出现的次数。前导 0 0 0不许出现。
输入格式:
输入包含多组测试数据。每组测试数据占一行,包含两个整数 a a a和 b b b。当读入一行为0 0
时,表示输入终止,且该行不作处理。
输出格式:
每组数据输出一个结果,每个结果占一行。每个结果包含十个用空格隔开的数字,第一个数字表示 0 0 0出现的次数,第二个数字表示 1 1 1出现的次数,以此类推。
数据范围:
1 ≤ a , b < 1 0 8 1\le a, b< 10^8 1≤a,b<108
有多组测试数据
我们可以开个函数,专门计算 [ 1 , x ] [1,x] [1,x]中 0 ∼ 9 0\sim 9 0∼9出现的次数,设这个函数是 f ( x ) f(x) f(x),那么 [ a , b ] [a,b] [a,b]之间所有数字中 0 ∼ 9 0\sim 9 0∼9出现的次数就是 f ( b ) − f ( a − 1 ) f(b)-f(a-1) f(b)−f(a−1)。所以只需要考虑怎么实现 f ( x ) f(x) f(x)这个函数。首先 f ( 0 ) = 0 f(0)=0 f(0)=0。接着考虑 x > 0 , f ( x ) x>0,f(x) x>0,f(x)。我们枚举 0 ∼ 9 0\sim 9 0∼9在各个位上出现的时候的计数。比如,我们想枚举 k ∈ { 0 , 1 , . . . , 9 } k\in \{0,1,...,9\} k∈{
0,1,...,9}在 x = a b c d e f g ‾ x=\overline{abcdefg} x=abcdefg这个数字的 d d d这个位置出现的情况下,有多少种可能。分两种情况讨论:
1、如果 k k k的左边是 0 ∼ a b c ‾ − 1 0\sim \overline{abc}-1 0∼abc−1,那么 k k k右边可以任意取, 0 ∼ 999 0\sim 999 0∼999都可以,此时方案数就是 1000 a b c ‾ 1000\overline{abc} 1000abc;
2、如果 k k k的左边是 a b c ‾ \overline{abc} abc,这个时候要看 k k k和 d d d的关系:
如果 k < d k<d k<d,那 k k k右边可以任意取,有 1000 1000 1000种情况;
如果 k = d k=d k=d,那 k k k右边只能取 0 ∼ e f g ‾ 0\sim \overline{efg} 0∼efg,有 1 + e f g ‾ 1+\overline{efg} 1+efg种情况;
如果 k > d k>d k>d,则没有可能, 0 0 0种情况;
但是对于 k = 0 k=0 k=0的情形需要另外考虑,因为不能有前导 0 0 0。首先枚举哪一位可以取 k k k的时候,要略过最高位(它本身就是前导 0 0 0了,要略过),接着,对于情况 1 1 1, k k k的左边的范围变成了 1 ∼ a b c ‾ − 1 1\sim \overline{abc}-1 1∼abc−1。注意考虑这个情况就行。代码如下:
#include <iostream>
#include <vector>
using namespace std;
// 求10的x次方
int pow10(int x) {
int res = 1;
while (x--) res *= 10;
return res;
}
// 将num表示的正整数
int get(vector<int> num, int l, int r) {
int res = 0;
for (int i = l; i >= r; i--)
res = res * 10 + num[i];
return res;
}
int count(int n, int x) {
if (!n) return 0;
// 逆序存储n的各个位
vector<int> num;
while (n) {
num.push_back(n % 10);
n /= 10;
}
n = num.size();
int res = 0;
// 从最高位开始枚举如果x是当前位的话有多少个可能性;如果x = 0的话要略过最高位
for (int i = n - 1 - !x; i >= 0; i--) {
if (i < n - 1) {
res += get(num, n - 1, i + 1) * pow10(i);
// 如果x = 0的话,其左边的范围要少1
if (!x) res -= pow10(i);
}
if (num[i] == x) res += get(num, i - 1, 0) + 1;
else if(num[i] > x) res += pow10(i);
}
return res;
}
int main() {
int a, b;
while (1) {
cin >> a >> b;
if (a == 0 && b == 0) break;
if (a > b) swap(a, b);
for (int i = 0; i < 10; i++)
cout << count(b, i) - count(a - 1, i) << ' ';
cout << endl;
}
return 0;
}
每次查询时间复杂度 O ( log 10 max { a , b } ) O(\log_{10}\max\{a,b\}) O(log10max{ a,b}),空间 O ( log 10 max { a , b } ) O(\log_{10}\max\{a,b\}) O(log10max{ a,b})。