题目地址:
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})。