1~n 整数中 1 出现的次数

题目:

输入一个整数 n, 求 1 ~ n 这 n 个整数的十进制表示中 1 出现的次数.

问题分析:

n 是最大的 k 位数时, 即 n=maxNumk

n=10k+11,sum(n)=k10k1

任何一位上的数字都可以是 09 (位数不足的在前面补 0 , 如四位数的1表示为 0001 ), 可知, 0n 共有 k10k 09 之间的数字, 每个数字出现的次数必然相等, 所以有上式成立. 可以用以下方式表示, 令

h(k)=10h(k1),h(1)=1

h(k)=10k1,k>0

故有

sum(maxNumk)=kh(k),k1

为了讨论的方便, 将 n 记为 (nknk1...n1), 其中 0ni9,nk0,i=k,k1,...,1 . 对于任意的 ni 有如下公式:

sum(ni)=ni(i1)h(i1)+0,nmodh(i)+1,h(i),ni=0ni=1ni>1

则可得

sum(n)=i=1ksum(ni)

案例:

n=10531

计算过程
个位数 1 : sum(11)=1(11)h(11)+nmodh(1)11=1+1=0+0+1=1 ;
十位数 3 : sum(32)=3(21)h(21)+h(2)32>1=3+10=13 ;
百位数 5 : sum(53)=5(31)h(31)+h(3)53>1=100+100=200 ;
千位数 0 : sum(04)=0(41)h(41)+004=0=0+0=0
万位数 1 : sum(15)=1(51)h(51)+nmodh(5)15=1+1=4000+531+1=4532

sum(n) 如下:

sum(n)=i=15sum(ni)=1+13+200+0+4532=4746

根据上述思想及公式, C++代码实现如下:

long NumberOf1(const int n)
{
    long sum = 0;
    int m = n;

    for (int k = 0, hk = 1; m > 0; ++k)
    {
        int modK = m % 10;
        sum = modK * k * (hk / 10);

        if (modK == 1)
            sum += n % hk + 1;
        else if (modK > 1)
            sum += hk;

        m /= 10;
        hk *= 10;
    }

    return sum;
}

该算法的时间复杂度为 O(logN), 空间复杂度为 O(1).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值