Number of Digit One -- leetcode

Given an integer n, count the total number of digit 1 appearing in all non-negative integers less than or equal to n.

For example:
Given n = 13,
Return 6, because digit 1 occurred in the following numbers: 1, 10, 11, 12, 13.




算法一,递归

1. 初始条件为,0~9时,1的个数为1

2. 当给定一整数,首先找到它的最高位的数字,同时求出其权重。

例如 4139, 最高位的数字为  4,  权重为 1000。  即4的每个单位代表 1000

3. 用递归的方式求出小于权重所有数中出现1出现的个数。如上例,countDigitOne(1000-1)。 其结果,记为count

4. 现在,小于等于4139中1出现的个数,一定包含  4 * count

即0XXX, 1XXX, 2XXX, 3XXX  我们通过步骤3求出了XXX中所能出现的1的个数。  X表示0~9

5. 特例是, 1XXX  还需要考虑最高位这个1,所出现的次数。

如果最高位>1,则1XXX中最高位1出现的个数为 权重, 此例中为 1000

如果最高位为1, 则是余数+1. 此例中 是 139 +1,   表示 1000 ~ 1139。 共 140个数

6. 最后再用递方式,求出余数中1出现的个数。

即countDigitOne(139)


总结为,计算 4139的话,

先求出4000以内1的个数, 即 0~999, 1000~1999, 2000~2999, 3000~3999   。  4 倍于 0~999

再求1XXX中最高位所带来的1的个数, 即1000~1999

最后求零头139内中1的个数,即0~139


计算 139时

先求 100以内1的个数, 即0~99,  1倍于0~99

再求1XX中最高位1所带来的个数,100~139, 共  40个。  注,上面用的是权重,此用的是余数+1.

最后求零头 39内1的个数。 即 0~39


class Solution {
public:
    int countDigitOne(int n) {
        if (n <= 0)
            return 0;
        if (n <= 9) 
            return 1;
            
        int m = 1;
        int msd = n;  // most significant digit
        while (msd > 9) {
            msd /= 10;
            m *= 10;
        }
        
        int count = countDigitOne(m-1);
        int total = 0;
        total += msd * count;
        if (msd > 1) {
            total += m;
        }
        else if (msd == 1)
            total += n % m + 1; // remainder
        total += countDigitOne(n % m);
        
        return total;
    }
};


算法二,统计每个位置上1出现的个数。

我们知道,十进制整数有个位,十位,百位,千位,万位。。。

在每个位置上,取值范围0~9. 

算法思路为,统计当一个位置取值为1时,在给定的范围内共有多少个可能的数字。

然后就是将所有这些统计值累加起来。即个位上为1的 + 百位上为1的 。。。。


1. 例如,给定范围为31415, 统计百位上为1时,共能有多少个数字。

即满足AA1XX这种模式,同时值小于等31415.     A,X都代表0~9

一共有多少个呢

此时,AA的可能取值范围为   00~ 31,   XX为 00~99.

故,共有 32 * 100 个数满足条件。 


2. 此处有一个边界条件需要考虑。比如当数字为31115,即百位上的数字,等于1时。

AA1XX  AA取值范围为 0~30,  不再是 0~31. 因为当AA取值31时,XX是不能取值0~99, 只能是 0~15

故要分成2部分来计算

第一部分同上,为  31 * 100 个数 , 即 AA 取值 0~30, XX 取值 0~99.  相对例子1来说,AA取值少了一个。

第二部分,当AA取值 31, XX取值0~15, 共有 16个数。


3. 第三个特例是,百位数上为0时,就不需要第二部分的计算。

比如 31015, 满足AA1XX的数, 只能是AA 0~30, XX 0~99  一种情况。   相对于例子1来说,AA取值少了一个。


即一个位置上的数,分成3种情况。即

0, 1, 2~9  


在下面程序中,0 和 2~9。 是两种情况一起处理的。 即通过  + 8  )/ 10这种运算.

(n/m + 8) / 10 * m

同时,在额外处理为1的情况。

(n / m % 10 == 1 ? n % m + 1 : 0)


class Solution {
public:
    int countDigitOne(int n) {
        int count = 0;
        for (long long m=1; m<=n; m*=10) {
            count += (n/m + 8) / 10 * m + (n / m % 10 == 1 ? n % m + 1 : 0);
        }
        return count;
    }
};


算法来源:

https://leetcode.com/discuss/44281/4-lines-o-log-n-c-java-python

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值