算法实现题 1-1 统计数字问题

统计数字问题

问题描述
给出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;
}

猜你喜欢

转载自blog.csdn.net/l1832876815/article/details/79559079