您的位置:首页 > 职场人生

Google面试算法题:《从1到n的正数中1出现的次数》的求解

2012-03-07 23:03 302 查看
问题描述:

在从1到n的正数中1出现的次数(数组)

题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。

例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。

注:这是一道广为流传的google面试题。

 

这道题也是摘自高手July的牛贴《横空出世,席卷Csdn:记微软等100题系列数次被荐[100题维护地址]》。这道题的求解其实并不难,一个一个数字去检查的这种brute force的方式也可以达到目的。不过这样的话效率太低,设input(dm
dm-1…… d2 d1)
,那么时间复杂度至少是O(10M)级别的。我想了一种方法,把问题先做了两次简化,在用简化问题的结果,组合成原问题的解,粗略的时间复杂度估计是O(m)级别的。

第一次简化:限定取值为:9, 99, 999等等,那么input简化为9的个数;

1)首先来想一下最简单的情况,n<=9。

1.1)那么n=0时,count(n)=0;

1.2)1<=n<=9时,count(n) =1。特别的count(9) =1。

简单的,我们n=0~9的结果存到一个10元数组中,以备查询。

int[] DICT = new int[] {0, 1, 1, 1, 1, 1, 1, 1, 1, 1};

 
acda

2)再稍微外推一点,n=99。从下图我们可以看到一点规律。

十位
个位
0
0~9

0~9
9
0~9
个位上是0~9的十次循环,那么1的个数就是10*count(9);而十位上是10个0,10个1,10个2,……10个9,我们把10当作因子提出来,可以发现1的个数是10*DICT[9]。那么count(99)= 10*count(9) + 10*DICT[9]

3)再稍微外推一点,n=999。同样的,下图验证了刚才我们发现的规律。

百位
十、个位
0
0~99

0~99
9
0~99
十位、个位一起可以看成0~99的十次循环,那么1的个数就是10*count(99);而百位上和刚才算99的时候十位上的情况差不多,所以1的个数是10*count(9)。那么我们就得到 count(999)=100*DICT[9] + 10*count(99)。

4)综上所述,我们可以得到一个递归公式count9(m), m该全9数字的位数,用来计算n=9, 99, 999, 9999……时1的个数:

4.1) count9(1) = 1;

4.1)count9(m) = 10 * count9(m-1)+ (10^(m-1)) * DICT[9]

java代码如下:

   public long countOneBy9(int p_NumOf9){

    if(p_NumOf9 == 0) {

       return 0;

    }

   

    int l_count = DICT[9];

    int l_yuXiang = DICT[9];

    for (int l_i = 1; l_i < p_NumOf9; l_i++) {

       l_yuXiang *= 10;

       l_count = l_count * 10 + l_yuXiang;

    }

   

    return l_count;

   }

 

第二次简化:稍微扩展一下input:19, 29, 39,……, 199, 299,……, 2999,……等等,可以利用1)的结果。

1)举个例子比如2999,0~299之间的数如下图:

百位
十、个位
0
0~999
1
0~999
2
0~999
类似的十位、个位上1的个数为10*count(999);而在百位上1的个数为:DICT[2]*1000。于是1的总个数为:countX9(2999)=3*count(999) + DICT[2]*1000。于是我们也得到了一个公式countX9(d, m),d为该数字的最高位,m为该数字中9的位数,用来计算n=d9,d99,d999等等情况下1的个数: countX9(d,m) =(d+1)*count9(m) + DICT[d]*(10^m)

java代码如下:

   public long countOneByX9(int p_x, int p_numOfFollowing9){

    if(p_x == 0 && p_numOfFollowing9 == 0) {

       return 0;

    }

   

    long l_count = (p_x + 1) * countOneBy9(p_numOfFollowing9);

    if(p_x != 0) {

       l_count += DICT[p_x] * ((long)Math.pow(10, p_numOfFollowing9));

    }

   

    return l_count;

   }

 

让问题回归复杂: 接受任意input(dm dm-1 …… d2d1)

那么我们可以把求解分为几段:

1) 0~(dm - 1)99999中1的个数。比如input是3230,我们先求解0~2999中1的个数。于是可以套用countX9(dm
- 1, m-1)。

2) (dm 0 …… 0 0)~ (dm dm-1 …… d2d1) 中1的个数。

  2.1)总共有(dm dm-1 …… d2d1) 个数字,其中最高为都是dm。那么如果dm=1,则我们需要在结果中加入(dm
dm-1 …… d2 d1)个1;

  2.2)去掉最高位后,我们可以递归的调用本函数求解。

java代码如下:

public class N030_GoogleCountOne extends N030_GoogleCountOnebyRange{

   public long countOne(int[] p_inputByDigits, long p_inputVal){

    int l_count = 0;

   

    int l_leadingIdx = p_inputByDigits.length - 1;

    //99999 Max, the rest is from 100000 to p_input

    int l_leadingDigit = p_inputByDigits[l_leadingIdx];

    l_count += countOneByX9(l_leadingDigit - 1, l_leadingIdx);

   

    long l_remainVal = p_inputVal -

        l_leadingDigit * (long)Math.pow(10, l_leadingIdx);

   

    l_count += (l_leadingDigit == 1 ? 1 : 0) * (l_remainVal + 1);

   

    int l_nextLeadingIdx = l_leadingIdx - 1;

    for (; l_nextLeadingIdx > -1 && p_inputByDigits[l_nextLeadingIdx] == 0;


        l_nextLeadingIdx--) {

       //find the next digit != 0

    }

   

    if(l_nextLeadingIdx != -1) {

       int[] l_remainInputByDigits = new int[l_nextLeadingIdx + 1];

       System.arraycopy(p_inputByDigits, 0, l_remainInputByDigits, 0,

           l_remainInputByDigits.length);

       l_count += countOne(l_remainInputByDigits, l_remainVal);

    }

   

    return l_count;

   }

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息