第一次自己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