页码统计解题报告

这里写图片描述

题目要求统计 09 每个数字出现的次数,可以基于经典题(统计 1n 1 的个数)来求解。

首先给出几个概念:
- 当前数位currNum,低位数字构成的数 lowerNum ,高位数字构成的数 upperNum 。引入它们的原因是对于当前数位,它能放置 1 的个数由这三个数确定。对于n=12345,若 currNum=3 ,则 lowerNum=45 upperNum=12
- 数位标志数 digitPos digitPos=1 表示个位, digitPos=10 表示十位,依次类推。

基于前面给出的几个概念,统计 1n 1 的个数的做法是这样的:

  1. 从个位开始遍历n的每一个数位,例如 n=322 时,依次遍历 2,2,3

    • 如果当前数位等于 1 ,则当前数位是1的个数等于 upperNum×digitPos+lowerNum+1 ,即此时 1 的个数同时由高位upperNum和低位 lowerNum 确定。例如 n=312 ,对于十位, currNum=1 upperNum=3 lowerNum=2 digitPos=10 ;此时在十位可能出现 1 的情况为1019 110119 210219 310312 ;即为 3×10+2+1
    • 如果当前数位小于 1 ,则当前数位是1的个数等于 upperNum×digitPos ,即此时 1 的个数仅由upperNum确定。例如 n=302 ,对于十位, currNum=0 upperNum=3 digitPos=10 ;此时在十位可能出现 1 的情况为1019 110119 ,, 210219 ;即为 3×10
    • 如果当前数位大于 1 ,则当前数位是1的个数等于 (upperNum+1)×digitPos ,即此时 1 的个数仅由upperNum确定。例如 n=322 ,对于十位, currNum=2 upperNum=3 digitPos=10 ;此时在十位可能出现 1 的情况为1019 110119 210219 310319 ;即为 (3+1)×10
    • 有了上面统计 1n 1 的个数的算法,我们很容易验证29也遵循这个规律。 0 有点小区别,但是也不用太担心,因为对它的求解也是基于上面的大框架。
      统计1n 0 的个数的做法是这样的:

      1. 从个位开始遍历n的每一个数位。注意最高位不需要处理,可以直接跳过,因为最高位不能为 0
      2. 如果当前数位等于0,则当前数位是 0 的个数等于upperNum1×digitPos+lowerNum+1,即此时 0 的个数同时由高位upperNum和低位 lowerNum 确定。例如 n=302 ,对于十位, currNum=0 upperNum=3 lowerNum=2 digitPos=10 ;此时在十位可能出现 0 的情况为100109 200209 300302 ;即为 31×10+2+1

      3. 如果当前数位大于 0 ,则当前数位是0的个数等于 upperNum×digitPos ,即此时 0 的个数仅由upperNum确定。例如 n=322 ,对于十位, currNum=2 upperNum=3 digitPos=10 ;此时在十位可能出现 0 的情况为100109 200209 300309 ;即为 3×10
      4. 容易看出该算法的时间复杂度是 O(lgn)
        有了上面统计 1n 0 19出现次数的算法,题目的解题思路已经很清晰了,下面给出Java下的实现代码:

        import java.util.Scanner;
        
        public class Main {
        
            public static int countOfNum(int n , int i) {   //1~n中出现数字i的次数
        
                int lowerNum = 0;   //保存低位数字
                int currNum = 0;    //保存当前位
                int upperNum = 0;   //保存高位数字
                int digitPos = 1;   //起始位数是个位   
                int count = 0;  //统计i出现的总次数
        
                while (n / digitPos != 0) {
        
                    lowerNum = n % digitPos;
                    currNum = (n / digitPos) % 10;
                    upperNum = n / (digitPos * 10);
        
                    if (i == 0) {   //i是0时特殊处理
                        if (n / (digitPos * 10) == 0)   //当前位是最高位,则返回
                            break;
                        if (currNum == i)
                            count += (upperNum - 1) * digitPos + lowerNum + 1;
                        else 
                            count += upperNum * digitPos;
                    }
                    else {  //i是1~9时统一处理
                        if  (currNum < i)
                            count += upperNum * digitPos;
                        else if (currNum == i)
                            count += upperNum * digitPos + lowerNum + 1;
                        else 
                            count += (upperNum + 1) * digitPos;
                    }
        
                    digitPos *= 10;
                }
        
                return count;
            }
        
        
            public static void main(String[] args) { 
        
                Scanner in = new Scanner(System.in);
                while(in.hasNext()) {
        
                    int n = in.nextInt();
                    for (int i = 0; i < 10; i++) {
                        int ret = countOfNum(n, i);
                        if (i == 9)
                            System.out.println(ret);
                        else
                            System.out.print(ret + " ");
                    }
                }
            }
        }
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值