统计页码数字问题

统计数字问题

问题描述:
一本书的页码从自然数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

解题思路1:根据输入的数字Num,从1到Num 一 一统计每个数字出现的次数。
算法时间复杂度: O(n*log10(n))

算法:
for(i =  1; i <= n; i++) 
{
   t = i;
    while(t) {
       count[t% 10]++;   //count数组是每个数字出现次数的计数器
       t/= 10;
   }
}

统计页码数字问题


f(n)=10*f(n-1)+10^(n-1)
f(n-1)=10*f(n-2)+10^(n-2)
f(n-2)=10*f(n-3)+10^(n-3)
             .
             .
             .
f(2)=10f(1)+10^1

可见:f(n)=10^(n-1)+(n-1)*10^(n-1)

即:f(n)=n*10^(n-1),利用此公式计算每个数字的出现次数,计算0的出现次数时,最后减去多余的前导0的出现次数即可。

举例(取区间法):
比如计算32871,从高位向低位看,可以看到该数包含3个0000~9999的区间(0~9999,10000~19999,20000~29999),即每个数字出现的次数为3f(4),其中多余的前导0出现在0000~9999区间中,经观察可知多了10^3+10^2+10^1+1个0。(0000-0999,10^3个0;000-099,10^2个0;00-09,10^1个0;开始的0,10^0个0)
其中,最高位上1,2共出现10^4次。接着计算30000~32871,此处最高位的3共出现2872次。于是,着眼点就落在0000~2871中各数字的出现次数上,此时依照上面的取区间法,一次一次去掉最高位,直到剩下个位时为止,这里不会有多余的前导0(你懂的)。

算法时间复杂度: log10(n)

inline  int Process( int n)   //f(n)=n*10^(n-1)原著公式
{
    return n*pow( 10,n- 1);
}

void Dispose( int a[], int n, int &c0) //页码处理过程
{
    int t=log10(n),temp=n; //假设输入数字为m位,t表示m-1
   
    while(t)
   {
       temp/=pow( 10,t); //temp为最高位数字
        if(temp!= 0)
       {
           n-=temp*pow( 10,t); //去除最高位数字
            for( int i= 0;i<= 9;i++)
               a[i]+=temp*Process(t); //每位数字加上m*f(m-1),即一个高位乘以低m-1位的区间
       }
       a[temp--]+=n+ 1; //高位数字因低m-1位数字的出现次数
    while(temp>= 0)a[temp--]+=pow( 10,t); //同上
       c0+=pow( 10,t); //需要去除多余0的个数
        //while与c0这两步可改为while(temp>0)a[temp--]+=pow(10,t);这样做在主函数中只需减去1个多余前导0
        if(n<10&&t== 1)
       {
            for( int i= 0;i<=n;i++)
               a[i]++;
           n= 0;
       }
       temp=n;
       t--;
   }
}

源代码:
#include
#include
#include
#include
#include
using  namespace std;

inline  int Process( int n) //f(n)=n*10^(n-1)原著公式
{
    return n*pow( 10,n- 1);
}

void Dispose( int a[], int n, int &c0) //页码处理过程
{
    int t=log10(n),temp=n; //假设输入数字为m位,t表示m-1
   
    while(t)
   {
       temp/=pow( 10,t); //temp为最高位数字
        if(temp!= 0)
       {
           n-=temp*pow( 10,t); //去除最高位数字
            for( int i= 0;i<= 9;i++)
               a[i]+=temp*Process(t); //每位数字加上m*f(m-1),即一个高位乘以低m-1位的区间
       }
       a[temp--]+=n+ 1; //高位数字因低m-1位数字的出现次数
        while(temp>= 0)a[temp--]+=pow( 10,t); //同上
       c0+=pow( 10,t); //需要去除多余0的个数
        //while与c0这两步可改为while(temp>0)a[temp--]+=pow(10,t);这样做在主函数中只需减去1个多余前导0
        if(n<10&&t== 1)
       {
            for( int i= 0;i<=n;i++)
               a[i]++;
           n= 0;
       }
       temp=n;
       t--;
   }
}

void write()
{
   fstream t;
   t.open( "F: \\ Input.txt",ios::out);
    if(!t)
   {
       cout<< "Can't open Input.txt"<         return
   }
   srand(( unsigned)time(NULL));
    for( int i= 0;i<20;i++)
       t<10000
<    t.close();
}

void main()
{
    int n,a[ 10]={ 0},c0= 0; //c0是多余的0的总数
   fstream f,fo;
   write();
   fo.open( "F: \\ Output.txt",ios::out);
    if(!fo)
   {
       cout<< "Can't open Output.txt"<         return
   }
   f.open( "F: \\ Input.txt",ios::in);
    if(!f)
   {
       cout<< "Can't open Input.txt"<         return
   }
    while(!(f>>n).eof())
   {
        if(n<10)
       {
            for( int i= 1;i<=n;i++)
               a[i]++;
            for( int i= 0;i<=n;i++)
               cout<             return;
       }
       Dispose(a,n,c0);
   

你可能感兴趣的:(平时学习的算法习题)