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

本文介绍了一种高效算法,用于统计1至n范围内每个数字的出现次数,特别适用于大数据范围(1≤n≤1e9)。通过数学推导得出f(n)=n*10^(n-1)的规律,并使用递归方法解决前导0的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

##统计数字问题##
问题描述
给出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));
    //判断n中是否存在0
    if(t == 0) {
        if(len-1 > 0) {
            cnt[0] += len-1;
        }    
        return;
    }
    else if(len - (log10(t)+1) > 1) {
        cnt[0]+=(len - (log10(t)+1)-1) * t;
    }
    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;
}


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chook_lxk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值