您的位置:首页 > 其它

统计数字问题

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的所有数,逐个统计。

代码实现:

/* 穷举所有数进行统计的算法 */
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),效率明显高于第一个算法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: