嘿。我知道这不是一个'重构我的代码'网站,但是我制作了这个小代码,它适用于中等大小的输入,但是它的大小字符串,例如超过2000个就有问题。
它的作用 - 它将一串数字作为参数,并返回可以解释为字母串的方式的数量,其中英文字母表中的每个字母根据其词汇分配一个数值位置:A - > 1,B-> 2,Z-> 26等。
由于某些字母表示为两个数字,因此后缀树不是唯一的,因此可以有多种解释。例如,'111'可以是'AAA','KA'和'AK'。
这是我的代码。它相当可读和直接,但它有问题,因为:
非常感谢:)
cache = dict()
def alpha_code(numbers):
"""
Returns the number of ways a string of numbers
can be interpreted as an alphabetic sequence.
"""
global cache
if numbers in cache: return cache[numbers]
## check the basic cases
if numbers.startswith('0'): return 0
if len(numbers) <= 1: return 1
## dynamic programming part
## obviously we can treat the first (non-zero)
## digit as a single letter and continue -
## '342...' -> C + '42...'
total = alpha_code(numbers[1:])
## the first two digits make for a legal letter
## iff this condition holds
## '2511...' -> Y + '11...'
## '3711...' -> illegal
if numbers[:2] <= '26':
total += alpha_code(numbers[2:])
cache[numbers] = total
return total
答案 0 :(得分:3)
尝试使用动态编程方法:
通过以下规则(伪代码)从左到右填充数组的其余部分:
P [x] = (如果当前字符为'0'则 0,其他 P [x-1]) + (如果以前的字符+当前字符可以解释为字母 然后 P [x-2] 其他 0)
(注意,如果P [x]为0,则应返回零,因为这意味着您的规则似乎不允许存在两个0。)
总和的第一部分是处理当前字符被解释为字母的情况;总和的第二部分是处理2个最近字符被解释为字母的情况。
基本上,P [x]将等于从开始到位置x 的整个字符串可以被解释为字母的方式的数量。既然您可以通过查看以前的结果来确定这一点,那么您只需要遍历字符串的内容一次 - 一个O(N)时间而不是O(2 N ),这是一个巨大的进步。你的最终结果只是P [len(输入)-1],因为“从开始到结束的所有内容”与“整个字符串”相同。
示例运行您的基本输入案例'111':
由于P [2]是我们的最后一个结果,而且它是3,我们的答案是3。
如果字符串是'1111',我们将继续另一步:
答案确实是5 - 有效的解释是AAAA,KK,AKA,AAK,KAA。请注意这5个潜在答案是如何从“11”和“111”的潜在解释构建的:
'11':AA或K. '111':AAA或KA或AK
'111'+ A:AAA + A或KA + A或AK + A. '11'+ K:AA + K或K + K
答案 1 :(得分:1)
递归消除总是一项有趣的任务。在这里,我将专注于确保正确填充缓存,然后使用它,如下所示......:
import collections
def alpha_code(numbers):
# populate cache with all needed pieces
cache = dict()
pending_work = collections.deque([numbers])
while pending_work:
work = pending_work.popleft()
# if cache[work] is known or easy, just go for it
if work in cache:
continue
if work[:1] == '0':
cache[work] = 0
continue
elif len(work) <= 1:
cache[work] = 1
continue
# are there missing pieces? If so queue up the pieces
# on the left (shorter first), the current work piece
# on the right, and keep churning
n1 = work[1:]
t1 = cache.get(n1)
if t1 is None:
pending_work.appendleft(n1)
if work[:2] <= '26':
n2 = work[2:]
t2 = cache.get(n2)
if t2 is None:
pending_work.appendleft(n2)
else:
t2 = 0
if t1 is None or t2 is None:
pending_work.append(work)
continue
# we have all pieces needed to add this one
total = t1 + t2
cache[work] = total
# cache fully populated, so we know the answer
return cache[numbers]
答案 2 :(得分:0)
可以编写非递归算法,但我认为它不会更快。我不是python专家,所以我只给你一个算法:
Convert the array on numbers to an array of letters using just A thru I and leaving the zeros in place.
Create two nested loops where you search and replace all the known pairs that represent larger letters. (AA -> K)
这个算法的优点是你可以通过首先搜索和索引数组中的所有As和B来优化搜索/替换。
由于您使用的是Python,无论您做什么,都应该将字符串转换为数字列表。数字0-9是Python中的静态对象,这意味着它们可以自由分配。您还可以创建A到Z的可重用字符对象。列表的另一个好处是删除两个元素的替换操作,插入单个元素比一遍又一遍地复制字符串要快得多。
答案 3 :(得分:0)
你可以通过不复制字符串来大大减少内存占用,而是传递原始字符串和要研究的第一个字符的索引:
def alpha_code(numbers, start_from = 0)
....
然后你会以递归方式调用:
alpha_code(numbers, start_from + 1) # or start_from + 2, etc.
这样,您可以保留递归算法的简单性并节省大量内存。