统计数字问题
2013-03-02 07:00
246 查看
题目出自《计算机算法设计与分析》王晓东编著。
问题描述:
一本书的页码从自然数1开始顺序编码直到自然数n。求的页码按照通常的习惯编排,每个页码都不含多余的前导数字0。例如第6页为6而非06或006等。试统计给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1,2,…,9。
给定表示书的总页码的十进制整数n(1≤n≤10^9),计算书的全部页码中分别用到多少次数字0,1,2,…,9。
解法一:
最容易想到的就是穷举1到n的所有数,逐个统计。
代码实现:
解法二:
发现当n分别为9,99,999,……,999999999时,数字1~9出现的次数分别均为1,20, 300,……,900000000。现在,我们任意给定一个数n,如2672。假设我们已经算出n=672时,数字1出现的次数为m。则n=2672时,会多出673~2672这些数。数字1出现的次数多出了300*2+1000=1600个,即当n=2672时,数字1出现的次数为m+1600。数字2的情况则不一样了,因为这时最高位的千位数为2,这时并没有多出1000个2,而是673个,所以当n=2672时,数字2出现的次数是多出了300*2+673=1273个。数字3~数字9的情况就又不一样了,这时最高位的千位数并没有出现数字3~9,所以这种情况下,数字出现的次数只多出了300*2=600个。数字0的情况较为特殊,按照数字1~9的情况进行计算,会多算出前导0,所以要把前导0减掉,而每一位多出的前导0是10^i个,i表示第i位数,所以len位数数字0就会多算出(10^len-1)/9个数,例如五位数就会多算出11111个数字零。所以,最后把多算出的前导零减掉就是正确的结果了。同理,计算n=672时,可依据n=72时的结果,以此类推。
代码实现:
问题描述:
一本书的页码从自然数1开始顺序编码直到自然数n。求的页码按照通常的习惯编排,每个页码都不含多余的前导数字0。例如第6页为6而非06或006等。试统计给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1,2,…,9。
给定表示书的总页码的十进制整数n(1≤n≤10^9),计算书的全部页码中分别用到多少次数字0,1,2,…,9。
解法一:
最容易想到的就是穷举1到n的所有数,逐个统计。
代码实现:
/* 穷举所有数进行统计的算法 */ void countNumber(int num, int results[10]) { int i; int temp; // 计数清零 for (i = 0; i < 10; i++) { results[i] = 0; } for (i = 1; i <= num; i++) { temp = i; while (temp > 0) { results[temp%10]++; temp /= 10; } } }这个算法的时间复杂度为n*log10(n)。
解法二:
发现当n分别为9,99,999,……,999999999时,数字1~9出现的次数分别均为1,20, 300,……,900000000。现在,我们任意给定一个数n,如2672。假设我们已经算出n=672时,数字1出现的次数为m。则n=2672时,会多出673~2672这些数。数字1出现的次数多出了300*2+1000=1600个,即当n=2672时,数字1出现的次数为m+1600。数字2的情况则不一样了,因为这时最高位的千位数为2,这时并没有多出1000个2,而是673个,所以当n=2672时,数字2出现的次数是多出了300*2+673=1273个。数字3~数字9的情况就又不一样了,这时最高位的千位数并没有出现数字3~9,所以这种情况下,数字出现的次数只多出了300*2=600个。数字0的情况较为特殊,按照数字1~9的情况进行计算,会多算出前导0,所以要把前导0减掉,而每一位多出的前导0是10^i个,i表示第i位数,所以len位数数字0就会多算出(10^len-1)/9个数,例如五位数就会多算出11111个数字零。所以,最后把多算出的前导零减掉就是正确的结果了。同理,计算n=672时,可依据n=72时的结果,以此类推。
代码实现:
#include <stdio.h> #include <stdlib.h> #include <math.h> void countNumber(int num, int results[10]) { int i, j; int v; int len = (int)log10(num) + 1; // num的长度 int weight[] = {0, 1, 20, 300, 4000, 50000, 600000, 7000000, 80000000, 900000000}; int digit[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; /* 计数清零 */ for (i = 0; i < 10; i++) { results[i] = 0; } for (i = 0; i < len; i++ ) { v = num / digit[i] % 10; // v表示第i位上的数 for (j = 0; j < 10; j ++){ if (j < v) { // j小于第i位上的数的情况 results[j] = results[j] + v * weight[i] + digit[i]; } if (j == v) { // j等于第i位上的数的情况 results[j] = results[j] + v * weight[i] + num % digit[i] + 1; } if (j > v) { // j大于第i位上的数的情况 results[j] = results[j] + v * weight[i]; } } } results[0] = results[0] - ((int)pow(10, len) - 1) / 9; // 减去前导零 } /* 打印结果到控制台 */ void printResult(int results[10]) { int i; for (i = 0; i < 10; i++) { printf("%d\n", results[i]); } } int main() { int num; int results[10]; printf("请输入一个正整数(0~1^10): "); scanf("%d", &num); if (num < 0 || num > 10000000000) { printf("输入有误!\n"); exit(0); } countNumber(num, results); printf("结果:\n"); printResult(results); return 0; }这个算法的时间复杂度为log10(n),效率明显高于第一个算法。