剑指offer43.1~n整数中1出现的次数

剑指offer43.1~n整数中1出现的次数_第1张图片

 看到这么大的数据规模就直到用暴力法肯定会超时,但是还是花一分钟写了一个试一下,果然超时

class Solution {
    public int countDigitOne(int n) {
        int count =0;
        for(int i=1;i<=n;i++){
            count+=digitOneInOneNum(i);
        }
        return count;
    }
    public int digitOneInOneNum(int n){
        int count =0;
        while(n != 0){
            if(n % 10 ==1)count++;
            n /=10;
        }
        return count;
    }
}

剑指offer43.1~n整数中1出现的次数_第2张图片

然后自己想了好多种方法,试了很多次都有没考虑到的地方,真的挺想自己做出来的,因为这道hrad题感觉没有那么难,但是写了一个多小时也没做出来,只好看题解了,题解的方法自己想到了一点点,就是去统计每一位上1出现的次数然后加起来,但是没把情况分明白。以下是题解代码:

class Solution {
    public int countDigitOne(int n) {
        // mulk 表示 10^k
        // 在下面的代码中,可以发现 k 并没有被直接使用到(都是使用 10^k)
        // 但为了让代码看起来更加直观,这里保留了 k
       int ans = 0;
       long mulk = 1;
       for(int i =0;n>=mulk;i++){
           mulk = (int)Math.pow(10,i);
           ans += (n/(mulk*10))*mulk + Math.min(Math.max(n % (mulk*10)-mulk+1, 0), mulk);
           mulk*=10;
       }
       return ans;
    }
}

我们统计每一位上1出现的次数,然后全部加起来就是0-n中1出现的次数,先以百位为例,然后扩展到任意位上。

以 n=1234567 为例,我们需要统计「百位」上数字 1出现的次数。我们知道,对于从 0 开始每 1000个数,「百位」上的数字 1都会出现 100次,即数的最后三位每 1000个数都呈现 [000,999]的循环,其中的 [100,199]在「百位」上的数字为 1,共有 100个。n有1234个这样的循环,所以百位上的1就出现了1234*100次,所以百位上1出现的次数就是[n/1000]*100,[]表示向下取整。

我们刚才考虑的是前面的1234会让后面出现1234次0-999的循环,导致百位出现了[1234/1000]*100个1,现在我们还要考虑后面的567让百位出现了几次1。首先拿1234567%1000就得到了后面的567,记n' = n%1000, 那么n‘在百位上1出现的次数可以分类讨论得出:

1.n' < 100: 百位上1出现的次数为0;

2.100<=n'<200: 百位上1出现的次数是n'-100+1;

3.n'>=200; 百位上1出现的次数是100;

综合这三种情况可以发现,n'导致百位上1出现的次数为:min(max(n′−100+1,0),100)

总的1出现的次数就是前面的1234导致的1出现加上后面的567导致的1出现的次数之和,也就是

                      ⌊1000n​⌋×100+min(max(nmod1000−100+1,0),100)

那么我们再把这个公式推广到任意位上,10的k次方位上(k=0,1,2时,表示个位,十位,百位)1出现的次数为:

所以子只要遍历每一个k(0,1,2……直到n的最高位)然后把每一位上1出现的次数加起来就可以了。

你可能感兴趣的:(剑指offer,算法,java,leetcode)