整数中1出现的次数(从1到n整数中1出现的次数)

时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M 

题目描述

求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
 
思路:
  最笨的方法,就是用两个循环,外部循环取数,内部循环取每位数上的值,判断是否为1,时间复杂度达到了O(n^2)级别
class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        if(n < 0)
            return 0;
        int count = 0;
        for(int i =n;i>0;i-- )
        {
            for(int j =i;j > 0;j /=10)
            {
                if(j %10 == 1)
                    count++;
            }
        }
        return count;
    }
};

  另一种方法就是找出规律,归纳出合理的公式

  先固定某位数的值为1,将一个数分为两部分,高位和低位。假设一个五位数n,用m来分割高低位,根据设定的整数位置,对n分割,分为两部分,高位n/m,低位就是n%m,分为三种情况,(再假设m所在位数为百位)

  1、当百位数>=2,如n = 31456,m = 100,则高位a = 314,低位b = 56

    100  ~ 199

    1100~1199
    …
    31100~31199

    此时百位为1 的次数有a/10 + 1 = 32(最高两位0~31),每一次都包含了100~199这100个连续的点,所以共有(a / 10 + 1) * 100个点的百位为1

  2、当百位数 ==1,如n = 31156,m = 100,则a = 311,b = 56  

    100  ~ 199

    1100~1199
    …
    31100~31156

    此时百位为1,共有a/10(最高两位0~30)次包含100个连续的点,即(a / 10) * 100 ,再加上局部点00~56,共有b+1次,即总共1的次数为(a/10)* 100 + b+1次

  3、当百位数 ==0时,如n=31056,m = 100,则a = 310,b = 56

    100  ~ 199

    1100~1199
    …
    31000~31056

    一共有(a/10) *100个1

  综上三种情况,当百位对于0或者>=2时,有(a+8)/10次包含有100个连续点,还有当百位==1(a%10 ==1)时,需要增加局部点b+1

  (之所以加8,是因为当百位数为0,则a / 10 = (a + 8)/10,当百位>=2,加8会产生进位,效果等同于(a/10 + 1),这样就可以将上述三种情况用一个表达式进行表达:

      count = (a + 8) // 10 * m + (a % 10 == 1) * (b + 1)

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        if(n < 0)
            return 0;
        int i = 1;
        int count = 0;
        for(i = 1;i <= n;i*=10)
        {
            int a = n/i,b = n%i;
            count += (a+8)/10*i + (a % 10 == 1)*(b+1);
        }
        return count;
    }
};

 

 

posted @ 2020-03-28 13:48  牛犁heart  阅读(270)  评论(0编辑  收藏  举报