【剑指offer】计算1至n中整数k出现的次数

参考文献

1、当k不为0时,符合以下规律

1、对于一个数字,我们以从右至左第i位(i从1数起)为分割线将它分为三部分{H}x{L},低位{L}共有i-1位数。固定x=k,高位{H}不变,当{L}取值没有其他限制时,则它的低位{L}每一位任意取值都能满足第i位为k,这样的前缀是{H}k的数字共有10^(i-1)个,我们将这样的一系列标记为L({H},i,k),简记为完整的L(i,k)系列。当{L}取值受到限制时,这样的数字只有L+1个,是一个不完整的L(i,k)系列。
2、对于给定的最大范围n={H}x{L},不含前缀{H}的完整L(i,k)有一个,含有{H}前缀的完整L(i,k)系列共有H或H-1个。当x小于k时,1至x{L}之间不可能含有L(i,k)系列,因此{H}不能取值H否则得到的数超出范围n。当x大于k时,1至x{L}之间可以有一个完整的L(i,k)系列,前缀{H}可以取值H且{L}取值不受限制。当x=k时,1至x{L}之间只有一个不完整的L(i,k)系列,前缀{H}可以取值H但是此时{L}的取值不能超过给定大小L,有0,1,2……L共L+1个。
3、以范围[1,2593]中5的个数为例,5???表示最高位是5的4位数字。

  • 当i=1时,H=259,x=3,L=0,范围[1,3]中不可能出现5,因此前缀只能取值0,1,2……258不能取值259,共259个完整的L(1,5)系列,即5在个位出现了259次。
  • 当i=2时,H=25,x=9,L=3,范围[1,93]中可以出现5?。因此前缀可以取值0,1,2……25,其26个完整的L(2,5)系列,即5在十位出现了260次。
  • 当i=3时,H=2,x=5,L=93,范围[1,593]中可以出现5??。因此前缀可以取值0,1,2,但是当前缀是2时,5??的取值不能大于593,即前缀是2时不是一个完整的L(3,5)系列,故5在百位出现了2*100+93+1次。
  • 当i=4时,H=0,x=2,L=593,范围[1,2593]中不能出现5???,因此5在千位出现了0次。

2、编程实现

根据上面的规律将数值n分成三部分,高位a=n/(10^i),高位余数t=n%(10^i),第i位x=t/(10^(i-1)),以及低位b=t%(10^(i-1))。
- 如果n的第i位数x小于k,结果为a*10^(i-1)。
- 如果n的第i位数x大于k,结果为(a+1)*10^(i-1)。
- 如果n的第i位数等于k,结果为a*10^(i-1)+b+1。

    public int countOfXBetween1AndN(int n,int x) {
        if(n<1||x<1||x>9) return 0;
        int high=n,tmp=0,cur=0,low=0,base=1,total=0;
        while(high!=0){
            high=n/(10*base);
            tmp=n%(10*base);
            cur=tmp/base;
            low=tmp%base;
            if(cur==x){
                total+=high*base+low+1;
            }else if(cur<x){
                total+=high*base;
            }else{
                total+=(high+1)*base;
            }
            base *=10;
        }
        return total;
    }

3、当k为0时,统计规律类似

  • 因为0不可能出现在首位,故{H}的取值只能是1到H,取值H时还需判断{L}的取值是否受限制。
  • 如果n的第i位数x不等于0,高位取值H时,低位可以取任何值都不会超过n,结果为a*10^(i-1)。
  • 如果n的第i位数x等于0,高位取值H时,低位{L}取值不能超过L,否则得出的数大于n,结果为(a-1)*10^(i-1)+b+1。
public int countOfZeroBetween1AndN(int n) {
        if(n<1) return 0;
        int high=n,tmp=0,cur=0,low=0,base=1,total=0;
        while(high!=0){
            high=n/(10*base);
            tmp=n%(10*base);
            cur=tmp/base;
            low=tmp%base;
            if(cur==0){
                total+=(high-1)*base+low+1;
            }else{
                total+=high*base;
            }
            base *=10;
        }
        return total;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值