求出 1 ~ 13 的整数中 1 出现的次数,并算出 100 ~ 1300 的整数中 1 出现的次数?可以发现 1 ~ 13 中包含 1 的数字有 1、10、11、12、13,因此共出现 6 次。现需要把问题更加普遍化,可以很快的求出任意非负整数区间中 1 出现的次数(从 1 到 n 中 1 出现的次数)
解题思路
设定整数点(1、10、100、1000、10000)分别对应 n 的个位、十位、百位、千位、万位,我们要做的就是:考虑每一位出现 1 的情况,算出各自符合题意的数的数量,并进行相加,就能得出想要的结果。
现以整数点 100 为例,使用 100 对 n 进行分割,高位为 n/100,低位为 n%100
考虑三种情况:
- 以 31456 为例,百位对应的数 4 >= 2,此时高位 a = 314,低位 b = 56,因为 4 >= 2,所以肯定会有百位为 1 的情况,此时无论其他位的数是多少,都将满足题意。高位可以取 32 种情况(0 ~ 31),低位则总是 100 个连续的点,因此共有 (a/10 + 1)*100 个数
- 以 31156 为例,百位对应的数 1 == 1,此时高位 a = 311,低位 b = 56,因为百位已经是 1,此时无论其他位的数是多少,都将满足题意。高位的取值要分情况,当最高两位范围是 0 ~ 30 时,和上一条一样;当 a = 311(取 31) 时,则对应的低位只有 0 ~ 56,共 b + 1 次,因此总共有 (a/10)*100 + (b + 1)
- 以 31056 为例,百位对应的数为 0,此时高位 a = 310,低位 b = 56,百位为 0,所以高位的前两位只能去 0 ~ 30,这样百位才能取 1,因此总共有 (a/10)*100
综合以上三种情况,我们可以得出:
- 当百位 == 0 或 >= 2 时,有 (a+8)/10 次包含所有 100 个点(之所以补 8,是因为当百位为 0,则 a/10 == (a+8)/10,当百位 >= 2,补 8 会产生进位位,效果等同于 (a/10+1))
- 当百位 == 1,即 (a%10==1),则需要增加局部点 b+1
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int count = 0;
long split = 1;
for(int i = 1; i <= n; i *= 10) {
int a = n/i, b = n%i;
count = count + ((a+8)/10)*i + ((a%10 == 1) ? (b + 1) : 0);
}
return count;
}
}