题目要求统计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,则当前数位是