统计数字问题
问题描述:
一本书的页码从自然数1 开始顺序编码直到自然数n。书的页码按照通常的习惯编排,每个页码都不含多余的前导数字0。例如,第6 页用数字6 表示,而不是06 或006 等。数
字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1,2,…,9。
编程任务:
给定表示书的总页码的10 进制整数n (1≤n≤109) 。编程计算书的全部页码中分别用到多少次数字0,1,2,…,9。
数据输入:
输入数据由文件名为input.txt的文本文件提供。每个文件只有1 行,给出表示书的总页码的整数n。
结果输出:
程序运行结束时,将计算结果输出到文件output.txt中。输出文件共有10行,在第k行
输出页码中用到数字k-1 的次数,k=1,2,…,10。
输入文件示例 输出文件示例
input.txt
11
output.txt
1
4
1
1
1
1
1
1
1
1
解题思路
这个题目不难,主要考察递归思想(当然也可以用其他形式代替递归),但这个题目也有一定的技巧性,思维灵活性要求较高。
最初,我看到这个题目的时候觉得很esay,不就是排列组合的问题嘛。把公式列出来O(1)的复杂度就可以解决,但当构思写伪代码的时候,发现被难住了,因为n不是确定的一个数,并且0-n的数据不等长,所以很难写出有效的排列组合公式。这里使用另一种方法来解决此问题。
由于不同位数的数字不等长,导致编程实现遇到非常不方便,我们采取迂回的路线,正面不行,则从侧面入手。首先,我们先让0-n的每一位数字的高位补上若干位0,数字大小不变,但长度和n的位数保持一致,这里我成为数字对齐。例如,n=423,则每一个数字补0后变成,000,001,..,010,..,422,423。
数字对齐后,进行分组,分成000-399,400-423这两个组,000-399可以用排列组合的方式很容易计算出出现多少个0(此时0的出现次数,多于最终要求的次数,因为有多余的前导0),1,2,..,9。然后在400-423这个组中,最高位4共出现24次,这个时候就需要统计的便是00-23中出现多少个0,1,..,9了,此时只要递归调用程序来处理23就行了(即原来n=423,一次递归之后,n=23)。
下面给出源代码
int a[10];
int exp(int n)
{
int sum=1;
for(int i=1;i<=n;i++)
sum*=10;
return sum;
}
void number_count(const int n)
{
if(n<=0)
return;
/**求出n的位数以及n的最高位数字*/
int high=0;
int num=0;
int tmp=n;
while(tmp>0)
{
sum++;
tmp/=10;
if(tmp/10==0)
high=tmp;
}
/*根据排列组合计算00..0-(hihg-1)99..9中出现的0-9的个数*/
int t1=exp(num-1);
int t2=exp(num-2);
for(int i=0;i<=9;i++)
{
if(i<high)
a[i]=t1+t2;
else a[i]=t2;
}
a[high]+=n-high*t1+1;
/*递归*/
number_count(n-high*t1);
/*去掉多计算的0*/
for(int i=1;i<num;i++)
{
a[0]-=(num-i)*exp(i);
}
}