统计计数_统计数字问题 解析

《计算机算法设计与分析》中第一道算法实现题:

题目大意: 一本书从1开始顺序编码到自然数n, 每个页码不含多余的0。 例如,第六页数字用6,而不是06。要求: 对给定的页码n, 计算出到该页码(包含该页码)共计多少个0,1,2,……,9

最容易想到的是暴力遍历,但是效率较低。书中给出递归的方法解决:

一. 递归思路:

  1. 阶段: 公式初步计算 -> 最高位(p)处理 -> 余项处理 -> 去零
  2. 详解:
    考察由0,1,2,……,9组成的所有n位数。 从n个0到n个9共有
    个n位数。在
    个n位数中,0,1,2,……,9 每个数字使用次数相同,设为f(n):

    即:

    据此,从高位向低位进行统计,即完成公式初步计算。
    最高位p处理: 对于[0, p-1]的每一位数字,都会增加
    (length 指的是n的长度)。余项为n去除最高为p后剩下的有意义的数值,例如,2019所产生的余项为19,9999产生的余项为999.

    余项处理: 如果余项为0,意味着当前数值为去除最高位后剩下的均为0,则
    · 最高位数字p的计数 +1,
    · 0的计数,增加 length - 1 (length 为n的长度)。
    之后,退出函数。
    若余项不为0且位数的长度不是 length -1, 则
    · 0的计数增加 (length - 1 -余项的长度)*(余项数值 + 1),即数值中间存在0的情况,例如2019 中间的 0
    · 最高位p的计数,增加 1 + 余项数值. 即最高位为p的数值z可能为0到余项数值这个范围,共计1 + 余项数值。
    若余项不为0且余项位数的长度是length - 1,则
    · 最高位p的计数,增加 1 + 余项数值.
    去零:这里要去除的为前几步多计算的0, 例如019中的第一个0.

134624f349e7f21ebe992031e1097530.png
多加的零

0的计数,要去除

3. 代码:

# 存储0到9数字的频数
c = [0 for i in range(10)]

def solve(n):
    msg = str(n)
    # 数字n的长度
    length = len(msg)
    # 首位数字 p
    p = int(msg[0])
    # 公式处理
    for i in range(10):
        c[i] += p * (length - 1) * int(pow(10, length - 2))
    # 最高位处理
    for i in range(p):
        c[i] += int(pow(10, length - 1))
    # 余项处理
    t = int(pow(10, length - 1))
    t = n%t
    if t == 0:
        c[p] += 1
        c[0] += length - 1
        return
    lenT = len(str(t))
    if lenT != length - 1:
        c[0] += (length - 1 - lenT)*(t + 1)
    c[p] += 1 + t
    return solve(t)

n = int(input())
solve(n)
# 去零
for i in range(0, len(str(n))):
    c[0] -= pow(10, i)
print(c)     

思路实现学习于 https://blog.csdn.net/m0_37579232/article/details/79651307

二。 Lintcode 中有一道变形题。题目:

LintCode 领扣​www.lintcode.com

思路借鉴于 小歪:萌新刷题(三)统计数字

有一点区别,题目中要求求出[0, n]中数字k的数量,只求某一数值出现的次数,且从0开始。

思路如下:

K:待检测数值

V: 当前数位值

Sum: 数值K出现的次数

这种方法依据的是规律:

1. 若 K > V, sum 增加 更高位 * 当前位数
例如 K = 2, 对于2019中的十位来讨论,此时V = 1, K > V, 更高位= 20, 更低位=9,sum += 更高位 * 当前位数 = 20 * 9

2. 若K == V, sum 增加 更高位 * 当前位数。 并且 若K != 0 或 当前讨论的是个位,sum 要多加 更低位 + 1。 注意这个问题中的0的处理。

3. 若K < V, sum 增加 更高位 * 当前位数, 且 若K != 0 或 当前讨论的是个位,sum 要 多加 当前位数。

至于规律的含义,不难理解。若实在不明究竞,可以边编码实现,边加深印象。

代码

class Solution:
    """
    @param k: An integer
    @param n: An integer
    @return: An integer denote the count of digit k in 1..n
    """
    def digitCounts(self, k, n):
        ans = 0
        # 字符串反转
        msg = str(n)[::-1] 
        for i in range(len(n)):
            // 当前数位
            cur = 10 ** i
            // 当前数值
            curValue = msg[i]
            // 更高位
            higher = n // (cur * 10)
            if k == curValue:
            // 更低位
                lower = n % cur
                ans += higher * cur + lower + 1
            elif k < curValue or k == 0:
                ans += higher * cur
            else:
                ans += (higher + 1) * cur
        return ans

三.统计数字问题,个人感觉更多的是寻找数学规律和善于分步骤解决问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值