从问题Number of Digit One延伸来,计算从1到给定的数字n共n个数字中,数字X出现的次数。

直观上来看,如果X是1-8之间的数字,那规律和之前在Number of Digit One 中分析的是一样的,因为对每个digit上的数字进行分析的话,都需要考虑三种情况,即给定的n上该位数字大于,等于和小于三种情况。如果X=0或9的话则稍稍不同,因为如果是X=0的话就只有大于和等于两种情况,而如果X=9的话就只有小于和等于两种情况。

当X为1-8间的数字:

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

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

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

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

 

当X=0:

给定n=nknk-1nk-2...n3n2n1,对于digit ni,分两种情况。

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

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

 

当X=9:

对于digit ni,分两种情况。

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

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

 

从结果上来看其实所有情况都可以合并为一种,毕竟X的取值范围本身就保证了当X=0和9时,需要排除的情况已经不会出现(即digit上不会有大于9或小于0的数字)。

代码跟计算1的个数的代码基本一样:

class Solution 
{
public:
    int countDigitOne(int n,int X) 
    {
        long num_of_digit_X=0,digit_num=1,higher=0,lower=0,cur=0;
        while(n>=digit_num)
        {
            higher=n/(digit_num*10);
            lower=n%digit_num;
            cur=(n/digit_num)%10;
            if(cur<X)
            {
                num_of_digit_X+=higher*digit_num;
            }
            else if(cur==X)
            {
                num_of_digit_X+=higher*digit_num+lower+1;
            }
            else
            {
                num_of_digit_X+=(higher+1)*digit_num;
            }
            digit_num*=10;
        }
        return num_of_digit_X;
    }
};

在Number of Digit One和Number of Digit X的基础上,题目还可以进一步延伸:

给定数字n和数字pattern m=m1m2...mk,计算该pattern从1到n中出现的次数。

example:

n=1234,m=123。则return 6,因为从1到1234出现123的数字为:

123,1230,1231,1232,1233,1234共6个。