统计数列中的数字出现次数

这篇博客讨论了如何统计1到n之间所有整数中0到9出现的次数。博主首先介绍了效率较低的算法,即对于每个n遍历一次。接着,提出了高效的算法,一次性遍历1到10000,并使用二维数组存储每个数字的出现次数。最后,还介绍了一种通过数字转字符串的方法来统计。样例输入和输出展示了算法的效果。

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

【题目描述】

把前n(n≤10000)个整数顺次写在一起:123456789101112…数一数0~9各出现多少次

(输出10个整数,分别是0,1,…,9出现的次数)。

输入第一行表示有T组数据,后续是T行数据n。依次输出前n个整数中0~9出现的次数,中间用空格隔开。

【样例输入】

2

3

13

【样例输出】

0 1 1 1 0 0 0 0 0 0

1 6 2 2 1 1 1 1 1 1

【题目来源】

刘汝佳《算法竞赛入门经典  第2版》习题3-3 数数字(Digit Counting , ACM/ICPC Danang 2007, UVa1225)

【解析】

前n个数相当于公差为1的等差数列,所以本题本质上就是统计数列中的各个数字出现的次数。

基本思路就是将每个整数分隔成一个个的数字k,然后用f[k]++记录数字k出现的次数。

一、效率较低的算法:每个n都遍历一次

就是针对输入的每个n,由1-n遍历、分离每个数求解。代码如下:

#include<stdio.h>
#include<string.h>
int main(){
    int T;
    int f[10];
    scanf("%d", &T);
    while(T--){
        memset(f, 0, sizeof(f)); //数组重置为0
        int n;
        scanf("%d", &n);
        for(int i=1; i<=n; i++){
            int t=i;
            //将数字t中各个数字出现次数累加到f[k]
            while(t){
                int k = t%10;
                f[k] ++;
                t /= 10;
            }
        }
        for(int k=0; k<9; k++){
            printf("%d ", f[k]);
        }
        printf("%d\n", f[9]);
    }
    return 0;
}

代码说明:

1.数组重置。由于涉及多组数据,为了避免上一组数据统计结果影响到下一组,需要在统计每组数据前将数组f[k]进行重置。

2.分离数字。采用“取尾法”将一个整数分离成各个数字。如想进一步了解详见“整数的分离与合成

3.空格隔开。这是一个初学者常掉的坑,但凡见到“用空格隔开数据”这样的表述一定要注意最后一个数字后不能输出空格。

二、高效算法:一次完全遍历,多次查找。

由于程序涉及到多组数据,像上面每输入一个n都要从1-n遍历一次显然效率比较低。更高效的做法是一次性从1-10000整个遍历一次,针对每个n,分别求出0-9出现的次数,然后把这些数据都存储到数组中。

这时候就需要用到二维数组f[n][k]统计数次,n表示前n个整数,k表示0-9。显然有f[n+1][k]=f[n][k]+x(x是k在整数n+1中出现的次数)

代码如下:

#include<stdio.h>
int f[10005][10];
int main(){
    for(int n=1; n<=10000; n++){
        //初始化f[n][k]
        for(int k=0; k<=9; k++)
            f[n][k] = f[n-1][k];

        int t=n;
        //将数字t中各个数字出现次数累加到f[n][k]
        while(t){
            int k = t%10;
            f[n][k]++;
            t /= 10;
        }
    }

    int T;
    scanf("%d", &T);
    while(T--){
        int n;
        scanf("%d", &n);
        //直接查找输出
        for(int k=0; k<9; k++){
            printf("%d ", f[n][k]);
        }
        printf("%d\n", f[n][9]);
    }
    return 0;
}

代码说明:

关于f[n+1][k]=f[n][k]+x的实现,老金最初也想用一个表达式实现,但是没有成功,只好退而求其次先用一个循环实现f[n+1][k]=f[n][k],不知道有没有高手有更好的方法,还望指点一二。

三、数字转字符串法

除了分离数字的方法,还有一种方法,就是将每个整数转为字符串,然后再遍历字符串中的每个字符。代码如下:

#include<stdio.h>
#include<string.h>
int f[10005][10];
int main(){
    for(int n=1; n<=10000; n++){
        //初始化f[n][k]
        for(int k=0; k<=9; k++)
            f[n][k] = f[n-1][k];

        char s[5];
        //整数转字符串
        sprintf(s, "%d", n);
        int len=strlen(s);
        //对字符串中的每个字符计数
        for(int k=0; k<len; k++){
            f[n][s[k]-'0']++;
        }
    }

    int T;
    scanf("%d", &T);
    while(T--){
        int n;
        scanf("%d", &n);
        //直接查找输出
        for(int k=0; k<9; k++){
            printf("%d ", f[n][k]);
        }
        printf("%d\n", f[n][9]);
    }
    return 0;
}

代码说明:

和方法二相比,其实只更改了统计字符个数的那部分代码。

关键是要用到数字转字符串的函数sprintf(s, "%d", n),它的用法和printf比较相似,只是增加了一个参数用来保存转换后的字符串。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

金创想

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

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

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

打赏作者

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

抵扣说明:

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

余额充值