统计数字问题
问题描述:
给出n,遍历1~n,统计每个数字的出现次数,没有前导0(1 <= n <= 1e9)。
思路:
数据范围太大,所以不能暴力。
一个n位数,从n个0到n个9一共10^n个数。在这n个数中,0~9出现次数相同(加上前导0),设为f(n).
当n = 1时, f(n) = 1;
当n > 1时,f(n) = 10 * f(n-1) + 10^(n-1);
由此可知:f(n) = n * 10^(n-1);
例如:23456
1. 除去最高位, 剩下的四位满足0000到9999的所有数,所以满足f(n)函数,最高位2,即答案 为2 * f(n) (0000~9999, 10000~19999);
2. 考虑最高位, 最高位为0,1分别是10000次。最高位为2时共有3456+1次。
3. 对余下的3456进行递归,当余下0时递归结束。
4.减去前导0的个数。
代码:
/*
f(n) = n * 10 ^ (n-1);
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 10 + 5;
int cnt[maxn];
int n;
void solve(int n) {
int len = log10(n)+ 1;//n的位数
//len-1位满足f(n)公式, 共n / (pow(10.0, len-1)) 个区间
int p = n / ((int)pow(10.0, len-1));
for(int i = 0; i <= 9; i++) {
cnt[i] += p * (len-1) * (int)pow(10.0, (len-2));
}
//最高位的数出现次数
for(int i = 0; i < p; i++) {
cnt[i] += (int)pow(10.0, (len-1));
}
cnt[p] += 1 + n % ((int)pow(10.0, len - 1));
//递归关系边界
int t = n % ((int)pow(10.0, len - 1));
if(t == 0) return;
else return solve(t);
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
while(cin >> n, n) {
fill(cnt, cnt + maxn, 0);
solve(n);
//减去前导0的个数
int len = log10(n) + 1;
for(int i = 1; i <= len; i++) {
cnt[0] -= (int)pow(10.0, (i - 1));
}
for(int i = 0; i <= 9; i++) {
if(i == 0) cout << cnt[i];
else cout << ' ' << cnt[i];
}
cout << endl;
}
return 0;
}