原题网址:https://leetcode.com/problems/number-of-digit-one/
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.
这道题需要注意的是统计的是十进制的1的个数,而不是二进制的1的个数!
思路:分别统计个位、十位、百位等各个数位上面1的个数,累加起来。假定n=915,
我们先来看个位,个位从0~9,每一段10个数字,其中有一个1,所以我们将n除以10,计算出有多少段,因为n/10=91,所以个位上有91个1,但要注意这里是整除,漏掉了910~915这一段,所以要将n对10取模,得到5,然后5大于等于1,因此需要补上这个1.
然后来看十位,十位数从0~9,每一段有100个数字,其中有10个1,所以我们将n除以100再乘以10,就可以计算出1的个数(91个),同样注意漏掉了910~915这一段,包含有5个1(911~915),如何得到这一段1的个数呢?方法是915对100取模减去10再加1,得到6。注意小于0的情况,例如n=108,则n%100-10+1=-1,也就是最后一段的十位数小于1,不需要计算进去。
再看百位,每一段有1000个数字,其中有100个1,所以我们将n除以1000再乘以100,就是百位上1的个数(915/1000*100=0个),然后再计算最末一段的1的个数,方法是对915对1000取模减去100再加上1(915%1000-100+1=816个),注意这个816的含义,他表示100~915有816个数,但我们知道每一段最多只能有100个1,所以只需要取Math.min(816,100)即可。
public class Solution {
public int countDigitOne(int n) {
final int ONE_TENTH_OF_MAX_VALUE = Integer.MAX_VALUE / 10;
int ones = 0;
int m;
for(m = 1; m <= n && m <= ONE_TENTH_OF_MAX_VALUE; m *= 10) {
ones += n / (m * 10) * m;
ones += Math.max(0, Math.min(n % (m * 10) - m + 1, m));
}
if (m <= n) ones += Math.max(0, Math.min(n - m + 1, m));
return ones;
}
}
一个简化的实现:
public class Solution {
public int countDigitOne(int n) {
int count = 0;
for(long a = 10, b = 1; b <= n; a *= 10, b *= 10) {
count += (int)(n / a * b + Math.max(0, Math.min(n % a - (b - 1), b)));
}
return count;
}
}