【剑指offer】--------整数中1出现的次数

第一次自己A出 leetcode 困难等级的题目
开始逐渐掌握如何把一个复杂问题化简为多个简单问题

题目描述

求出1到13的整数中1出现的次数,并算出100到1300的整数中1出现的次数?为此他特别数了一下1到13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
  

解法1:由特殊到普遍

这个是我自己的想法,过程比较复杂
遵循化繁为简的原则,首先需要分离出各个位数,将各个位数含1的数量进行累加,即可得到最终的结果
例如:
输入21345,可以分解为以下子问题:
1~5:
1~40:
1~300:
1~1000:
1~20000:

第二步:
总结规律,尝试找到递推关系
1~9: 1
1~99: 10 * 1 + 10 = 20 (这里10*1代表1, 11,21…这种情况取个位有10个1,此外还有十位为1的数,取十位有10个1,注意取个位和取十位是什么意思,下面的以此类推)
1~999: 20 * 10 + 100 = 300
1~9999: 300 * 10 + 1000 = 4000

规律很明显,设这个数有i个9,那么就存在 i*10i - 1 个1
把这些作为往后推理的基础存入base_list中
接下来就可以利用base_list中已知的结果来计算,例如输入230
可以拆分为:
1~30:1 * 3 + 10
1~200:2 * 20 + 100 (这里的加100是加[100, 199]中百位1个个数)
就得到了最终结果153

但存在特殊情况,当存在位数为1的情况时:
例如输入1345:
1~5:1
1~40:4 * 1 + 10
1~300:20 * 3 + 100
1~1000:300 + 1
最终结果为576?
错!
这是没考虑到1000之后千位上的1算出的结果
最终结果应加上千位带来的1:
576+345=821

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        if n == 0:
            return 0
        base = list()
        for i in range(13):
            base.append(pow(10, i) * (i + 1))
        bit_list = list()
        while n >= 1:
            bit_list.append(n % 10)
            n = n // 10
        result = 0
        ge = bit_list.pop(0)
        temp_add = ge
        if ge >= 1:
            if len(bit_list) > 0 and bit_list[0] == 1:
                result += 1 + ge
            else:
                result += 1
        for i in range(len(bit_list)):
            temp = bit_list.pop(0)
            temp_add += temp * 10 ** (i + 1)
            if temp != 0:
                if temp == 1:
                    result += base[i] + 1
                else:
                    result += temp * base[i] + 10 ** (i + 1)
                if len(bit_list) > 0 and bit_list[0] == 1:
                    result += temp_add
        return result

解法2:
分别统计各个位数中1的个数
这种方法代码简洁很多

def NumberOf1Between1AndN_Solution(n):
    temp = n
    res = 0
    base = 1                            # 应该是指位数,1表示个位,10表示十位....
    while temp:
        p = temp % 10                   # 取出当前位
        temp = temp // 10
        res += temp * base              # temp * base得到该位的出现次数
        if p == 1:
            # 如果这个位数是1,那么还要算上这个位数1出现的次数
            # 举例: 1141的百位
            # 上面temp * base即1 * 100算出的是1000前百位为1的个数,1100到1141百位上的1没统计到,因此最终结果还要加上42个
            res += n % base + 1
        elif p > 1:
            # 如果这个位数大于1,那么这个位数的1还出现了base次
            # 举例: 1141的十位
            # 上面temp * base即11 * 10算出的是1100前十位为1的个数,1100后还有10个没统计到
            res += base
        base *= 10
    return res

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值