题目要求统计 0∼9 每个数字出现的次数,可以基于经典题(统计 1∼n 中 1 的个数)来求解。
首先给出几个概念:
- 当前数位
- 数位标志数
digitPos
,
digitPos=1
表示个位,
digitPos=10
表示十位,依次类推。
基于前面给出的几个概念,统计 1∼n 中 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 的情况为10∼19 , 110∼119 , 210∼219 , 310∼312 ;即为 3×10+2+1 。 - 如果当前数位小于
1
,则当前数位是
1 的个数等于 upperNum×digitPos ,即此时 1 的个数仅由upperNum 确定。例如 n=302 ,对于十位, currNum=0 , upperNum=3 , digitPos=10 ;此时在十位可能出现 1 的情况为10∼19 , 110∼119 ,, 210∼219 ;即为 3×10 。 - 如果当前数位大于
1
,则当前数位是
1 的个数等于 (upperNum+1)×digitPos ,即此时 1 的个数仅由upperNum 确定。例如 n=322 ,对于十位, currNum=2 , upperNum=3 , digitPos=10 ;此时在十位可能出现 1 的情况为10∼19 , 110∼119 , 210∼219 , 310∼319 ;即为 (3+1)×10 。 有了上面统计 1∼n 中 1 的个数的算法,我们很容易验证
2∼9 也遵循这个规律。 0 有点小区别,但是也不用太担心,因为对它的求解也是基于上面的大框架。
统计1∼n 中 0 的个数的做法是这样的:- 从个位开始遍历
n 的每一个数位。注意最高位不需要处理,可以直接跳过,因为最高位不能为 0 。 - 如果当前数位等于
0 ,则当前数位是 0 的个数等于(upperNum−1)×digitPos+lowerNum+1 ,即此时 0 的个数同时由高位upperNum 和低位 lowerNum 确定。例如 n=302 ,对于十位, currNum=0 , upperNum=3 , lowerNum=2 , digitPos=10 ;此时在十位可能出现 0 的情况为100∼109 , 200∼209 , 300∼302 ;即为 (3−1)×10+2+1 。 - 如果当前数位大于
0
,则当前数位是
0 的个数等于 upperNum×digitPos ,即此时 0 的个数仅由upperNum 确定。例如 n=322 ,对于十位, currNum=2 , upperNum=3 , digitPos=10 ;此时在十位可能出现 0 的情况为100∼109 , 200∼209 , 300∼309 ;即为 3×10 。 容易看出该算法的时间复杂度是 O(lgn) 。
有了上面统计 1∼n 中 0 和1∼9 出现次数的算法,题目的解题思路已经很清晰了,下面给出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 + " "); } } } }
- 从个位开始遍历
- 如果当前数位等于
1
,则当前数位是