算法Week02.04 - LeetCode 233. Number of Digit One

题目大意

计算小于或等于给定数字的正整数中数字 1 的个数。

解题思路

我们来观察几个规律。
先看数字个位中 1 出现次数的规律:

(0), 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29

容易观察到,每10个非负整数,个位中就会出现 1 个数字 1。
以此类推,每100个非负整数,十位中就会出现 10 个数字 1。

10, 11, 12, 13, 14, 15, 16, 17, 18, 19, …
… 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, …

这些规律很容易理解,但是给定的数字不一定都满足整十整百整千的规律。一般的数字有两种情况,一种是截断了连续出现数字 1 的区间,另一种是不截断连续出现数字 1 的区间。

个位情况

个位每10个非负整数只出现 1 次,不存在所谓的截断问题。计算个位出现的数字 1 时只需要计算 n / 10,然后再判断 n 的个位是不是大于 0。

十位情况

与个位不同,十位上的数字 1 最长连续出现 10 次,因此需要考虑截断的问题。判断十位上出现的数字 1 时,除了要计算 10 * (n / 100) 以及判断 n 的后两位是不是大于等于 10 以外,还要考虑比 10 大多少。

具体说来,如果末两位是 15,那么在十位上则发生了截断的情况,十位出现的数字 1 还有 (15 - 10 + 1) 个;如果末两位是 23,则没有发生截断的情况,十位上出现的数字 1 还有 10 个。

用公式表达出来就是 ex = n % 100 - 10 + 1; if (ex > 10) ex = 10;

能够理清十位的情况,继续往上也就没有难度了。

完整代码

class Solution {
public:
    int countDigitOne(int n) {
        if (n < 0) return 0;

        int cnt = 0;
        long long base = 10;
        int tmp;

        while (n >= base) {
            tmp = n / base;
            cnt += (tmp * base / 10);
            if (base * tmp + base / 10 <= n) {
                tmp = n - base * tmp - base / 10;
                tmp += 1;
                if (tmp > base / 10) tmp = base / 10;
                cnt += tmp;
            }
            base *= 10;
        }
        tmp = n / base;
        cnt += (tmp * base / 10);
        if (base * tmp + base / 10 <= n) {
            tmp = n - base * tmp - base / 10;
            tmp += 1;
            if (tmp > base / 10) tmp = base / 10;
            cnt += tmp;
        }
        return cnt;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值