统计数字问题
2012-03-29 22:23
183 查看
【座右铭】1. 想要成为行家,就必须尝试解决大量的问题;
2. 解决大量问题并不代表能解决所有问题,而是表示解决下一个问题的几率变大了
1.一本书的页码从自然数1开始顺序编码直到自然数n。书的页码按照通常的习惯编排,每个页码都不含多余的前导0。例如第6页,而不是06页,或者006页。要求给定的页码n,统计出全部页码中出现了多少次0,1,2,...,9
第一部分:思路
1. 对于数字n=a1a2a3...ak,假设页码包含前导0,从左到右统计,统计方法如下
1.1 将a1a2a3...ak分为如下部分:
1) 从000..0(k个0)到 (a1-1)99...9之间0-9出现的次数
2) 再将a1的次数加上:a2a3...ak + 1次
3) 再递归统计n=a2a3...ak中0-9出现的次数
举例说明:n=263
1) 先统计000-199之间0-9出现的次数
2) 再将2的次数加上:63+1=64次
3) 再递归统计n=63时0-9出现的次数
1.2 统计000..0(k个0)到(a1-1)99...9之间0-9出现的次数如下:
1) 最高位0,1,2...(a1-1)都出现了10^(k-1)次
2) 在余下的k-1位中,出现了a1次从00..0(k-1个)到99..9(k-1个)。因为0-9出现的次数相等,因此0-9分别出现了 a1*(k-1)*10^(k-1)/10
举例说明:从000-199
1) 最高位0和1都出现了100次
2) 然后00-99出现了2次,因此0-9分别出现了2*2*100/10=40次
2.减去前导0,k位数中前导0的数目如下:
1个数:全是0,如00...00。推出k个前导0
9个数:非0数从倒数第1位开始,如00...0X。推出(k-1)*9个前导0
90个数:非0数从倒数2位开始,如00...0XX。推出(k-2)*90个前导0
900个数:非0数从倒数第3位开始,如00...0XXX。推出(k-3)*900个前导0
依次轮推
第二部分:Java代码,不考虑异常
第三部分:测试用例
n=1,0到9出现的次数:0 1 0 0 0 0 0 0 0 0
n=11,0到9出现的次数:1 4 1 1 1 1 1 1 1 1
n=100,0到9出现的次数:11 21 20 20 20 20 20 20 20
20
n=2001,0到9出现的次数:494
1601 602 600 600 600 600 600 600 600
n=67321,到9出现的次数:26162
37273 37264 37184 37162 37162 34484 26484 26162 26162
2. 解决大量问题并不代表能解决所有问题,而是表示解决下一个问题的几率变大了
1.一本书的页码从自然数1开始顺序编码直到自然数n。书的页码按照通常的习惯编排,每个页码都不含多余的前导0。例如第6页,而不是06页,或者006页。要求给定的页码n,统计出全部页码中出现了多少次0,1,2,...,9
第一部分:思路
1. 对于数字n=a1a2a3...ak,假设页码包含前导0,从左到右统计,统计方法如下
1.1 将a1a2a3...ak分为如下部分:
1) 从000..0(k个0)到 (a1-1)99...9之间0-9出现的次数
2) 再将a1的次数加上:a2a3...ak + 1次
3) 再递归统计n=a2a3...ak中0-9出现的次数
举例说明:n=263
1) 先统计000-199之间0-9出现的次数
2) 再将2的次数加上:63+1=64次
3) 再递归统计n=63时0-9出现的次数
1.2 统计000..0(k个0)到(a1-1)99...9之间0-9出现的次数如下:
1) 最高位0,1,2...(a1-1)都出现了10^(k-1)次
2) 在余下的k-1位中,出现了a1次从00..0(k-1个)到99..9(k-1个)。因为0-9出现的次数相等,因此0-9分别出现了 a1*(k-1)*10^(k-1)/10
举例说明:从000-199
1) 最高位0和1都出现了100次
2) 然后00-99出现了2次,因此0-9分别出现了2*2*100/10=40次
2.减去前导0,k位数中前导0的数目如下:
1个数:全是0,如00...00。推出k个前导0
9个数:非0数从倒数第1位开始,如00...0X。推出(k-1)*9个前导0
90个数:非0数从倒数2位开始,如00...0XX。推出(k-2)*90个前导0
900个数:非0数从倒数第3位开始,如00...0XXX。推出(k-3)*900个前导0
依次轮推
第二部分:Java代码,不考虑异常
/** * 一本书的页码从自然数1开始顺序编码直到自然数n。书的页码按照通常的习惯编排,每个页码都不含多余的前导0。 * 例如第6页,而不是06页,或者006页。要求给定的页码n,统计出全部页码中出现了多少次0,1,2,...,9 * @param page * 页码 * @return * 一个长度为10的数组,分别表示0,1,2...9出现的次数 */ public static int[] statistic(int page) { //数字不超过20位 final int integerLen = 20; //保存数位 int[] dbit = new int[integerLen]; int len = 0; while(page>0) { dbit[len++] = page%10; page /= 10; } //用于步骤1.1中的第2步 int[] dnum = new int[integerLen]; dnum[0] = 0; int base = 1, k = 1; for(;k<len;k++) { dnum[k] = dbit[k-1]*base + dnum[k-1]; base *= 10; } k = 0; for(;k<len;k++) { dnum[k]++; } //步骤1 int[] result = new int[10]; k = len; base = 1; while((--k)>0) { base *= 10; } k = len; while((--k)>=0) { int curbit = dbit[k]; //步骤1.2中第1步 int j = 0; for(;j<curbit;j++) { result[j] += base; } //步骤1.1中第2步 result[j] += dnum[k]; //步骤1.2中第2步 base /= 10; j = 0; int add = base*k*curbit; for(;j<10;j++) { result[j] += add; } } //步骤2:减去前导0 k = 1; base = 9; result[0] -= len; for(;k<len;k++) { result[0] -= (len-k)*base; base *= 10; } return result; } /* * 蛮力法:用于调试 */ static int[] statistic2(int page) { int[] result = new int[10]; for(int i=1;i<=page;i++) { int j = i; while(j>0) { result[j%10]++; j /= 10; } } return result; }
第三部分:测试用例
n=1,0到9出现的次数:0 1 0 0 0 0 0 0 0 0
n=11,0到9出现的次数:1 4 1 1 1 1 1 1 1 1
n=100,0到9出现的次数:11 21 20 20 20 20 20 20 20
20
n=2001,0到9出现的次数:494
1601 602 600 600 600 600 600 600 600
n=67321,到9出现的次数:26162
37273 37264 37184 37162 37162 34484 26484 26162 26162