剑指Offer:整数中1出现的次数(从1到n整数中1出现的次数)

题目描述

求出1-13的整数中1出现的次数,并算出100-1300的整数中1出现的次数?
为此他特别数了一下1~13中包含1的数字有1、10、11、12、13,因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

解析

这个方法可以求1~n的所有整数中(1,2,3,4,5,6,7,8,9出现的所有次数):
通过使用一个位置乘子 m 遍历数字的位置, m 分别为1,10,100,1000…etc.(m<=n)
例如n=3141592,对于每个位置来说,分成前后两部分,比如在百位上,分为a=31415 和 b=92 ;
百位上的数字是5,大于1,那么在百位上的数字为1的时候,它的前面有0-3141种取值,
它的后面有0-99种取值,所以一共有3142*100种取值。
再比如在千位上,分为a=3141 和 b=592;百位上的数字正好是1,那么它的前面取值为0-313时,
后面可以有0-999种取值,但是当前面取值为314时,后面最大只能取到592,所以一共有314*1000+593种取值。

如何判断各位置上的数是否大于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,…以此类推)。
注意:只有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)…以此类推)
public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        int count=0;
        int m=1;
        int a=0;
        int b=0;
        for(;m<=n;m*=10){
            a = n/m;
            b = n%m;
            count+= a%10==1?(a+8)/10*m+(b+1):(a+8)/10*m;
	    //(a+8)/10来判断当前位置是否大于1,后面乘以m 就是后面的数取0-999的所有情况
	    //然后如果当前位置的数等于1,就加上后缀的所有情况
        }
        return count;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_43165002/article/details/89513391