《计算机算法设计与分析》中第一道算法实现题:
题目大意: 一本书从1开始顺序编码到自然数n, 每个页码不含多余的0。 例如,第六页数字用6,而不是06。要求: 对给定的页码n, 计算出到该页码(包含该页码)共计多少个0,1,2,……,9
最容易想到的是暴力遍历,但是效率较低。书中给出递归的方法解决:
一. 递归思路:
- 阶段: 公式初步计算 -> 最高位(p)处理 -> 余项处理 -> 去零
- 详解:
考察由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.
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
三.统计数字问题,个人感觉更多的是寻找数学规律和善于分步骤解决问题。