题目:Given an integer n, count the total number of digit 1 appearing in all non-negative integers less than or equal to n. (LeetCode 233)

Hint: 提示放在最后面了,想看的话拉到底

For example:
Given n = 13,
Return 6, because digit 1 occurred in the following numbers: 1, 10, 11, 12, 13.

参考框架:

class Solution 
{
public:
    int countDigitOne(int n) 
    {
        //code
    }
};

对于一时之间无法直接看出规律的,先分析具体例子,从中找规律。

假设n=432105,把各个digit分开考虑,即假设某一digit为1时,其他digits能有多少种变化,则可以知道针对这一digit,从1-n会有多少个1出现。

假设考虑432105中的千位数字。当千位数字为1时,从1到n中多少个结构为XX1YYY的数字出现(注意X和Y可以为0)。

n=432105,千位数字为2,因为1<2,所以XX取值范围为00-43共44种取值。同样,由于1<2,YYY的取值范围为000-999共1000种取值。则从1到n,千位上出现1的次数为44×1000=44000个。

现在考虑432105中的百位数字。同样地,考虑有多少个结构为XXX1YY的数字。

所给n=432105中百位数字为1。对于从1到431099,XXX的取值范围为000-431共432种。而YY的取值范围为00-99共100种。对于从432000到432105的范围中,XXX只能为432这1种取值。而YY的取值范围为00-05共6种。则从1到n,百位上出现1的个数为432×100+6=43206个。

再考虑十位上的数字0,寻找结构为XXXX1Y的数字的个数。

从1到432099,当十位数字为1时,XXXX的取值范围为0-4320共4321种取值,而Y的取值为0-9共10种取值。而从432100到432105,XXXX只能是4321,Y为0-5。但从432100到432105,十位数字没有为1的可能性,则从1到n,十位上出现1的个数为4321×10=43210。

依次类推,可以得出对于数字n=432105,6个digits上出现1的个数分别为:

6th digit : 100000

5th digit : 50000

4th digit : 44000

3rd digit : 43206

2nd digit : 43210

1st digit : 43211

total number of 1 : 323627

从对于具体数字的分析可以看到,为了寻找数字1的个数,在分析每个digit时,要以1为分界线分别分析,因为若给的数字n在这个digit上比1大,则比这个digit高位的数字可以从0取到n的高位数字,但是比1小则不行,比如以上例子n=432105中的十位,高位数字范围不能从0到4321,因为43211X超过了n。而当某digit为1时,高位数字虽然可以取到最大范围,但是低位数字在abc1XX(abc代表n的高位数字)范围内不能取到最大值。

综上,要计算从1到n数字1的个数,对n=nknk-1nk-2...n3n2n1的每个digit分别分析,对于digit ni,分三种情况。

若ni>1,则ith digit上出现1的个数=(nknk-1...ni+1 + 1)×10i-1

若ni=1,则ith digit上出现1的个数=nknk-1...ni+1×10i-1+ni-1ni-2...n1+1

若ni<1(即ni=0),则ith digit上出现1的个数=nknk-1...ni+1×10i-1

最后把所有digit上出现的1的个数加起来就得到了最终结果。

C++代码如下:

 1 class Solution 
 2 {
 3 public:
 4     int countDigitOne(int n) 
 5     {
 6         long num_of_digit_one=0,digit_num=1,higher=0,lower=0,cur=0;
 7         while(n>=digit_num)
 8         {
 9             higher=n/(digit_num*10);
10             lower=n%digit_num;
11             cur=(n/digit_num)%10;
12             if(cur==0)
13             {
14                 num_of_digit_one+=higher*digit_num;
15             }
16             else if(cur==1)
17             {
18                 num_of_digit_one+=higher*digit_num+lower+1;
19             }
20             else
21             {
22                 num_of_digit_one+=(higher+1)*digit_num;
23             }
24             digit_num*=10;
25         }
26         return num_of_digit_one;
27     }
28 };

问题还可以推广到number of digit X,计算从1-n,特定数字(X=0到9)出现的次数:

number of digit X,X=0-9

 

 

 

 

 

Hint:

  1. Beware of overflow.