剑指offer43.1~n整数中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;
    }
}

然后自己想了好多种方法,试了很多次都有没考虑到的地方,真的挺想自己做出来的,因为这道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出现的次数加起来就可以了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

荔枝味啊~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值