题目: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:
- Beware of overflow.