数字统计问题

本文介绍了如何解决数字计数问题,即给定一本书的总页码n,计算书中所有页码中各个数字0到9的出现次数。通过分析页码的数字组成,提出了一种算法思路,从高位到低位逐位统计数字出现的次数,并在最后排除前导0的影响。算法避免了遍历每个页码,提高了效率。作者分享了个人的思考过程和实现关键,并表示会继续探索从低位开始统计的方法。

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

1.问题描述
一本书的页码从自然数1开始顺序编码直到自然数n。书的页码按照通常的习惯编排,每个页码都不含多余的前导数字0。例如,第6页用数字6表示,而不是06或006等。数字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1,2,…,9。

2.算法设计
给定表示书的总页码的10进制整数n(1≤n≤109)。编程计算数的全部页码中分别用到多少次数字0,1,2,…,9。

3.数据输入
输入数据由文件名为input.txt的文本文件提供,每个文件只有1行,给出表示书的总页码的整数n。

4.结果输出
将计算结果输出到文件output.txt。输出文件共有10行,在第k行输出页码中用到数字k-1的次数,k=1,2,3,…,10。

5.求解问题的算法描述

{ 在一个n位十进制数的由低到高的第i位上,总是连续出现10^i个0, 10^i个1, ……, 10^i个9, 9之后又是连续的10^i个0, 10^i个1, ……, 10^i个9,这样循环出现。由此可以在第i位上求出每个数字出现的次数,而在第i个数位上,最前面的10^i个0是前导0,应该在统计的时候减掉。}
以上是我百度到的一段算法解析,没看太明白……

以下是我的分析:
我把页码看成是一个字符串,也就是 一个N位数 PageNum = AB…Z(其中0≤ A,B…,Z ≤9 ),比如A=a, B=b, …, C=c(0≤ a,b, … , z ≤9)

0.在A位置上:
number < a : count[number] += 10^(N-1);
number = a : count[number] += bc…z + 1;
number > a: count[number] += 0;
1.在B位置上:
number < b: count[number] += 10^(N-2) * (a+1);
number = b: count[number] += 10^(N-2) * a + (cd…z + 1);
number > b: count[number] += 10^(N-2) * a
2.在C位置上:
number < c: count[number] += 10^(N-3) * (ab+1);
number = c: count[number] += 10^(N-3) * (de…z + 1);
number > c: count[number] += 10^(N-3) * ab;
……
i.在I位置上:
number < i: count[number] += 10^(N-i-1) * (i前面的数字 + 1);
number = i: count[number] += 10^(N-i-1) * (i后面的数字 + 1);
number > i: count[number] += 10^(N-i-1) * i前面的数字;

PS:以上计算的时候并没有讨论0的情况,而是把0006中0出现的次数一起统计了进去,因此在最后需要去掉前导0的计数!
(思考这题的时候我其实是想从低位开始统计的,这样就可以不用考虑前导0 的问题,但是思考到一半还是换到了从高位开始,我想从低位开始找规律应该也是可行的,有空的时候我再从低位开始找规律的分析下)

(这个过程就像是小学时候的做奥数题,找规律一样~ 奥数题的话只要能找到规律算出答案就行了,但是算法呢还需要考虑得算法复杂度等,虽然不知道我写的算法算不算效率高的, 至少没有去一个一个数字遍历~)

6. 算法实现的关键技巧
在算法中要尤其注意每次循环中对最高位的计算,以及最后要减去前导0的个数。

7.实验分析及结论

public static void main(String[] args) {
        int page = 0;
        Scanner input;
        Formatter output;

        //读取页码数
        try {
            input = new Scanner(Paths.get("input.txt"));
            if(input.hasNext()){
                page = input.nextInt();
            }
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //统计数字
        int count[] = count(page);

        //写入文件
        try {
            output = new Formatter("output.txt");
            for(int num : count)
                output.format("%d%n", num);
            output.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }

    public static int[] count(int page){
        //初始化计数数组
        int count[] = new int[10];
        for(int i = 0; i < 10; i++){
            count[i] = 0;
        }

        //得出页码数的位/长度
        String str = Integer.toString(page);
        int len = str.length();

        int pre = 0;//前面最高位前面的数字
        int now = 0;//当前的最高位
        int rem = 0;//当前最高位后面的数字

        for(int i = 0; i < len; i++){
            rem = page % (int)(Math.pow(10.0, len-i-1));
            now = page / (int)(Math.pow(10.0, len-i-1));
            //计算小于当前最高位的数字在这个位置出现的次数
            for(int j = 0; j < now; j++)
                count[j] += Math.pow(10.0, len-i-1) * (pre + 1);
            //计算当前最高位的数字在这个位置出现的次数
            count[now] += Math.pow(10.0, len-i-1) * pre + rem + 1;
            //计算大于当前最高位的数字在这个位置出现的次数
            for(int j = now + 1; j <= 9; j++)
                count[j] += Math.pow(10.0, len-i-1) * pre;

            //将当前最高位加到前面的数字中
            pre = pre * 10 + now;
            //去掉当前最高位得到的数,也就是前面的余数
            page = rem;

        }

        //去掉对前导0的计数
        for(int i = 0; i < len; i++){
            count[0] -= Math.pow(10.0, len-i-1);
        }

        return count;
    }

以上就是我的第一题算法题~
希望自己可以坚持下去 :)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值