剑指offer-31:整数中1出现的次数(从1到n整数中1出现的次数)
目录
1题目描述
求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。
ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数。
2题目解析及答案
class Solution {
public:
int NumberOf1Between1AndN_Solution(int n){
int ones = 0;
for (long long m = 1; m <= n; m *= 10)
ones += (n/m + 8) / 10 * m + (n/m % 10 == 1) * (n%m + 1);
return ones;
}
};
- int a = n/m, b = n%m;
- 注解:参考leetcode(包括求1~n的所有整数中2,3,4,5,6,7,8,9出现的所有次数)
- 通过使用一个位置乘子m 遍历数字的位置, m 分别为1,10,100,1000…etc.(m<=n)
对于每个位置来说,把10进制数分成两个部分
(1)比如说当m=100的时候, 把十进制数 n=3141592 分成 a=31415 和 b=92 ,以此来分析百位数为1时所有数的个数和。
m=100时,百位数的前缀为3141
当百位数大于1时,为3142*100,因为当百位数大于1时,前缀可以为0,即百位数可以从100到199,共100个数;
当百位数不大于1时,为3141*100;
如何判断百位数是否大于1?
假设百位数为x,若(x+8)/10等于1,则大于1,若(x+8)/10等于0,则小于1。
因此前缀可用(n/m + 8)/10 *m来计算(若计算2的个数,可以改为(n/m + 7)/10*m,若计算3的个数,改为(n/m + 6)/10*m,…以此类推)。
(2)再例如m=1000时,n分为a=3141和 b=592;
千位数的前缀为314,千位数不大于1,故前缀计算为314*1000;
因为千位数为1,再加b+1(0到592)。即千位数为1的所有书的个数和为314*1000+592+1;公式(n/m + 8)/10*m + b +1。
- 注意:只有n的第m位为1时需要计算后缀,后缀计算为 (n/m%10==1)*(b+1)
即(n/m%10==1)判断第m位是否为1,若为1,则加上(b+1),若不为1,则只计算前缀。
(若计算2的个数,可以改为(n/m%10==2)(b+1),若计算3的个数,可以改为(n/m%10==3)(b+1)…以此类推)