LeetCode Algorithms 233. Number of Digit One 题解

来源:互联网 发布:wow7.0优化 编辑:程序博客网 时间:2024/06/05 14:07

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

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

题目意思就是求前n个非负整数里面出现1的次数

显然可以从0遍历到n,然后每个数字都求一次1的次数这样来解决,这是一个O(n)的解决方案,算不上很慢,但是看起来就觉得这是最差的方法。

通过观察可以发现,当我们按位数的个数来划分时,

  • 位数是1时是0-9,1的个数为1,记为a1
  • 位数是2时是10-99,1的个数为10+a1+8xa1=19,记为a2
  • 位数是3时是100-999,1的个数为100+a1+a2+8x(a1+a2)=280,记为a2
  • ……

这里解释一下位数是2时是怎么数1的:

因为是数1,所以10-19首先包含十位数的10个1,数完这10个十位数的1后,把10-19看成0-9来数(不需要再数十位数的1),即a0,然后20-99,因为十位数都不是1,所以全部看成0-9来数,共8个a0,加起来就是10+a0+8xa0=19

同理,位数是3时,100-199有100个1,然后看成0-99来数,这个刚刚已经数过了是a1+a0,而200-999,因为百位数不是1,全部看成0-99来数,共8x(a1+a0)。

因为是求和,用an做记号不方便,设Sm=a0+a1+……+am,其中a0表示n<0时,且a0=0,那么:

  • 位数是1时,1的个数为S1
  • 位数是2时,1的个数为S2
  • ……
  • 位数是m时,1的个数为Sm

根据前面的观察,写出Sm的递推式子:
Sm=10^(m-1)+(a0+a1+...+a(m-1))+8x(a0+a1+...+a(m-1))+(a0+a1+...+a(m-1))
其中最后一项(a0+a1+...+a(m-1))表示加上位数更小的结果,这样才算完整的0-999...99(m个9)的1的个数。化简Sm后:

Sm=10^(m-1) + 10Sm-1

求得:
Sm=m10^(m-1)

到这里我们可以轻松的得到前n=999…999个非负整数的1的个数,显然这样是不够的,n可以是任意非负整数,比如n=23。面对这种情况,我们模仿Sm的推理过程,把23分成0-9,10-19,20-23这三段来计算,那么n=23时,1的个数={0-9}S1+{10-19}10^1+S1+{20-23}(2-2)xS1+S1=13,

可见需要用到每个位数上的数字,因此使用一个数组N保存每个位数上的数字,对于一个m位的n

n=N[0]+10N[1]+...+10^(m-1)N[m-1]

从最高位开始,每一个位数以及后面的数字当成一段来计算,这样第i段的1的个数可以写为:

S(m-1)+10^(m-1)+(N[i]-1)xS(m-1)

计算完这段之后把该位置0,
这里需要注意两点:
1. 当N[i]=1时,不能直接计算10^(i-1),因为没有包含10…0-19…9中所有整数,这里10^(m-1)+(N[i]-1)xS(m-1)需要换成数字1后面的那串数字+1(比如123,就是换成23+1),然后进行下一段的计算。
2. 当N[i]=0时,应该跳过然后进行下一段的计算。

把以上算法写成代码如下:

int countDigitOne(int n) {    /* S(n) = 10 * (10^(n - 2) + S(n-1))        where S(n) is the sum of 1 of the number of n digits        and it follows that S(0) = 0 digits S(1) = 1 ... S(n) = n * 10^(n - 1)    */    if (n <= 0) return 0;    if (n < 10) return 1;    int res = 0;    int tmp = n;    int digits = 0; // digits of n    vector<int> S; // Sn    vector<int> N; // N[i] represents the number of (i - 1) digit    while (tmp) {        N.push_back(tmp % 10);        tmp /= 10;        S.push_back(digits++ * pow(10, digits - 1));    }    while (!N.empty()) {        if (N.back() == 0) {            N.pop_back();            continue;        }        res += S[N.size() - 1];        if (N.back() > 1) {            int m = N.size() - 1;            res = res + pow(10, m) + (N.back() - 1) * S[m]; // S[m] + (m - 2) * S[m]            n -= N.back() * pow(10, N.size() - 1);        }        else {            res = res + (n -= N.back() * pow(10, N.size() - 1)) + 1;        }        N.pop_back();    }    return res;}
原创粉丝点击