题目
给定整数n,算出[1,n]所有数字(0-9)出现个数
#include<iostream>
#include<cmath>
using namespace std;
const int MAXN = 10;
int f[MAXN];
void init() {
for(int i = 1; i < MAXN; i++) {
f[i] = i * pow(10, i-1);
}
}
void print(int *ans) {
for(int i = 0; i < 10; i++) {
cout << i << ": " << ans[i] << endl;
}
}
int main(void) {
int n;
init();
while(cin >> n) {
int ans[10] = {0};
int len = log10(n) + 1; //数字位数
int tempLen = len;
while(len) {
int t = pow(10, len-1);
int high = n / t; //最高位
for(int i = 0; i <= 9; i++) {
ans[i] += f[len-1]*high;
}
for(int i = 0; i < high; i++) {
ans[i] += t;
}
ans[high] += n - high*t + 1;
n = n % t;
len--;
}
for(int i = 0; i < tempLen; i++) { //去除前导0
ans[0] -= pow(10, i);
}
print(ans);
}
return 0;
}
思路
计算前导0,最后删除前导0
0–9 每个数字出现 1次
00–99 每个数字出现 20次
000–999 每个数字出现 300 次
n个0–n个9 每个数字出现 n10(n-1)
故f(n) = n10(n-1);
例:35789
00000–09999 ①
10000–19999 ②
20000–29999 ③
30000–35789 ④
区间① ② ③除最高位外可按规律计算ans[i] += f[len-1]high, len = log10(n)+1是数字位数,high位n的最高位
最高位单独算ans[i] += 10(len-1).
区间④最高位ans[high] = n % (high10(len-1)) + 1。
成功将区间缩小为 0000–5789,每次循环可将位数缩减1位,循环到位数为0即可
最后减去前导0
0–9 前导0的个数为1
00–99 前导0个数为11
000–99 前导0个数为111
与位数有关 100+101+102+……+10(len-1)