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.
Hint:
- Beware of overflow.
这道题让我们比给定数小的所有数中1出现的个数,之前有道类似的题 Number of 1 Bits,那道题是求转为二进数后1的个数,博主开始以为这道题也是要用那题的方法,其实不是的,这题实际上相当于一道找规律的题。那么为了找出规律,我们就先来列举下所有含1的数字,并每10个统计下个数,如下所示:
1的个数 含1的数字 数字范围
1 [1, 9]
11 0 2 3 4 5 6 7 8 9 [10, 19]
1 2 [20, 29]
1 3 [30, 39]
1 4 [40, 49]
1 5 [50, 59]
1 6 [60, 69]
1 7 [70, 79]
1 81 [80, 89]
1 9 [90, 99]
11 00 0 02 03 04 05 06 07 08 09 [100, 109]
21 0 2 3 4 5 6 7 8 9 [110, 119]
11 20 2 22 23 24 25 26 27 28 29 [120, 129]
... ... ...
通过上面的列举可以发现,100 以内的数字,除了10-19之间有 11 个 ‘1’ 之外,其余都只有1个。如果不考虑 [10, 19] 区间上那多出来的 10 个 ‘1’ 的话,那么在对任意一个两位数,十位数上的数字(加1)就代表1出现的个数,这时候再把多出的 10 个加上即可。比如 56 就有 (5+1)+10=16 个。如何知道是否要加上多出的 10 个呢,就要看十位上的数字是否大于等于2,是的话就要加上多余的 10 个 '1'。那么就可以用 (x+8)/10 来判断一个数是否大于等于2。对于三位数区间 [100, 199] 内的数也是一样,除了 [110, 119] 之间多出的10个数之外,共 21 个 ‘1’,其余的每 10 个数的区间都只有 11 个 ‘1’,所以 [100, 199] 内共有 21 + 11 * 9 = 120 个 ‘1’。那么现在想想 [0, 999] 区间内 ‘1’ 的个数怎么求?根据前面的结果,[0, 99] 内共有 20 个,[100, 199] 内共有 120 个,而其他每 100 个数内 ‘1’ 的个数也应该符合之前的规律,即也是 20 个,那么总共就有 120 + 20 * 9 = 300 个 ‘1’。那么还是可以用相同的方法来判断并累加1的个数,参见代码如下:
解法一:
class Solution {
public:
int countDigitOne(int n) {
int res = , a = , b = ;
while (n > ) {
res += (n + ) / * a + (n % == ) * b;
b += n % * a;
a *= ;
n /= ;
}
return res;
}
};
解法二:
class Solution {
public:
int countDigitOne(int n) {
int res = ;
for (long k = ; k <= n; k *= ) {
long r = n / k, m = n % k;
res += (r + ) / * k + (r % == ? m + : );
}
return res;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/233
类似题目:
Digit Count in Range
参考资料:
https://leetcode.com/problems/number-of-digit-one/
https://leetcode.com/problems/number-of-digit-one/discuss/64390/AC-short-Java-solution
https://leetcode.com/problems/number-of-digit-one/discuss/64381/4+-lines-O(log-n)-C++JavaPython