Google 的一道面试题的解法

Google 的一道面试题的解法

Consider a function which, for a given whole number n, returns the number of ones required when writing out all numbers between 0 and n.

For example, f(13)=6. Notice that f(1)=1. What is the next largest n such that f(n)=n?

有一个整数n,写一个函数f(n),返回0n之间出现的"1"的个数。比如f(13)=6,现在f(1)=1,问下一个最大的f(n)=nn是什么?

为什么f(13)=6, 因为1,2,3,4,5,6,7,8,9,10,11,12,13.数数1的个数,正好是6.

程序一【未经过优化,效率低的惊人】

#include <windows.h>

main()

{

    int i,begin,end,temp=0,num=0;

    begin=GetTickCount();

    for(i=1;i<1111111110;i++)

    {

        temp=i;

        while(temp)

        {

              if(temp%10==1)num++;

              temp=temp/10;

        }

        if(num==i) printf("f(%d)=%d/n",i,num);

    }

    end=GetTickCount();

    end-=begin;

    printf("time: %d ms/n",end);

    system("pause");

}

程序二:【稍微经过优化的算法,不过效率也不高吆】

#include <windows.h>

main()

{

    int i,begin,end,temp=0,num=0,num1,num2;

   int Number[1000];

    begin=GetTickCount();

   for(i=0;i<1000;i++)

   {

      temp=i;

        while(temp)

        {

              if(temp%10==1)num++;

              temp=temp/10;

        }

      Number[i] = num;

      num = 0;

   }

   num = 0;

    for(i=0;i<1111111111;i++)

    {

        temp=i;

      while(temp)

      {

        num1 = temp/1000;

        num2 = temp - num1*1000;

        num += Number[num2];

        temp = num1;

      }

        if(num==i) printf("f(%d)=%d/n",i,num);

    }

    end=GetTickCount();

    end-=begin;

    printf("time: %d ms/n",end);

    system("pause");

}

程序三:【优化后的算法,效率明显的提高】

#include <windows.h>

#include <stdlib.h>

#include <stdio.h>

 

int f(int n);

int count1(int n);

int cal(unsigned int number,int nwei,int count1,unsigned int ncount);

int gTable[10];

const unsigned int gMAX = 4000000000L;

int main(int argc, char* argv[])

{

    int i;

    unsigned int n=1;

    unsigned int ncount = 0;

    int nwei = 0;

    int ncount1;

    int beginTime=GetTickCount();

    //init gTable

    for(i=0;i<10;++i)

    {

        n *= 10;

        gTable[i] = f(n-1);

    }

    n=0;

    nwei = 0;

    ncount1 = 0;

    while(n<gMAX)

    {

        unsigned int temp;

        temp = 1;

        ncount =cal(n,nwei,ncount1,ncount);

        for(i=0;i<nwei;++i)

            temp *= 10;

        n += temp;

        if( (n/temp)/10 == 1)

            ++nwei;

        ncount1 = count1(n);

    }

    int endTime=GetTickCount();

    endTime-=beginTime;

    printf("time: %d ms/n",endTime);

    system("pause");

    return 0;

}

int f(int n)

{

    int ret = 0;

    int ntemp=n;

    int ntemp2=1;

    int i=1;

    while(ntemp)

    {  

        ret += (((ntemp-1)/10)+1) * i;

        if( (ntemp%10) == 1 )

        {

            ret -= i;

            ret += ntemp2;

        }

        ntemp = ntemp/10;

        i*=10;

        ntemp2 = n%i+1;

    }

    return ret;

}

int count1(int n)

{

    int count = 0;

    while(n)

    {

        if( (n%10) == 1)

            ++count;

        n /= 10;

    }

    return count;

}

int cal(unsigned int number,int nwei,int count1,unsigned int ncount)

{

    int i,n=1;

    unsigned int maxcount;

    if(nwei==0)

    {

        ncount += count1;

        if(number == ncount)

        {

            printf("f(%d) = %d /n",number,number);

        }

        return ncount;

    }

    for(i=0;i<nwei;++i)

        n *= 10;

    maxcount = ncount + gTable[nwei-1];

    maxcount += count1*n;

    if(ncount > (number + (n-1)) )

    {

        return maxcount;

    }

    if(maxcount < number)

    {

        return maxcount;

    }

    n /= 10;

    for(i=0;i<10;++i)

    {

        if(i==1)

            ncount = cal(number+i*n,nwei-1,count1+1,ncount);

        else

            ncount = cal(number+i*n,nwei-1,count1,ncount);

    }

    return ncount;

}

程序四【另类解法,提供一个思路,不过效率是个问题吆】

/*

粗略的看上去以为需要逐位通过某种规则去求解,可是仔细一想,是否可以这么考虑。

一个数字:xyz,从1数到他需要1,2,3,。。。,xyz

转换一个思路,那么就是001,002,003,。。。,xyz

因为我们要计数的是1出现的次数,加上0并不影响结果。

如果这个数是999999,即一个全部由9组成得数字,那么之间经过的数中,0出现几率1/10,1出现几率是1/10,,,,9出现几率是1/10。

再扩展一下,一个数x999999,即除第一位外都由9组成,那么1出现的次数就是(x99999+1)×(1/x)+后面的位数×(x99999+1)×(1/10) 也就是说最高位出现1的可能性就是最高位的倒数,其他各位出现0-9的几率都是相等的。

那 么现在如果有一个数,我们就可以先求得小于本身最接近的x999999这样的数,那么从0-这个x999999数的1出现的次数可以直接用上面的公式求 的,然后(本身-x9999999)这个数再求从0到他出现的1个个数就可以了。(当然这个还得考虑最高位是否为1的特殊情况)

*/

#include <stdio.h>

int countones(int number)

{

    if(number<10)

        return number==0?0:1;

    int result=0;

    int base=10;

    int count=0;

    while(number/base!=0)

    {

        base*=10;

        count++;

    }

    base=base/10;

    int highbit=(int)(number/base);

    int x=count*base/10;

    result+=(highbit==1)?x:base+x*highbit;

    int left=number%base;

    int temp=countones(left);

    if(highbit==1)

    {

        result+=left+1;

    }

    result+=temp;

    return result;

}

 

main()

{

    int i;

    for(i=0;i<1111111111;i++)

    {

        if(countones(i)==i)printf("f(%d)=%d/n",i,countones(i));

    }

}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值