LeetCode Algorithms 233. Number of Digit One 题解
2017-06-05 13:20
190 查看
题目: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时是
位数是2时是
位数是3时是
……
这里解释一下位数是2时是怎么数1的:
因为是数1,所以
同理,位数是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的个数为
位数是2时,1的个数为
……
位数是m时,1的个数为
根据前面的观察,写出Sm的递推式子:
其中最后一项
求得:
到这里我们可以轻松的得到前n=999…999个非负整数的1的个数,显然这样是不够的,n可以是任意非负整数,比如n=23。面对这种情况,我们模仿Sm的推理过程,把23分成0-9,10-19,20-23这三段来计算,那么n=23时,1的个数={0-9}
可见需要用到每个位数上的数字,因此使用一个数组N保存每个位数上的数字,对于一个
从最高位开始,每一个位数以及后面的数字当成一段来计算,这样第
计算完这段之后把该位置0,
这里需要注意两点:
1. 当N[i]=1时,不能直接计算10^(i-1),因为没有包含10…0-19…9中所有整数,这里
2. 当N[i]=0时,应该跳过然后进行下一段的计算。
把以上算法写成代码如下:
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; }
相关文章推荐
- 【LeetCode】233. Number of Digit One
- 233. Number of Digit One
- [LeetCode]233. Number of Digit One
- Leetcode 233. Number of Digit One
- 233. Number of Digit One
- [LeetCode]233. Number of Digit One
- Hard-题目26:233. Number of Digit One
- LeetCode 233. Number of Digit One
- leetcode No233. Number of Digit One
- leetcode hard模式专杀之233. Number of Digit One
- [Leetcode] 233. Number of Digit One 解题报告
- leetcode 233. Number of Digit One
- 算法Week02.04 - LeetCode 233. Number of Digit One
- leetcode 233. Number of Digit One
- 233. Number of Digit One
- 233. Number of Digit One
- Leetcode 233. Number of Digit One
- 【LeetCode】233. Number of Digit One
- 233. Number of Digit One
- leetcode 233. Number of Digit One