我一直在研究dynamic programming problem involving the justification of text。我相信我找到了一个可行的解决方案,但我对此算法的运行时感到困惑。
到目前为止我所做的研究已经将这个问题的动态规划解决方案描述为 O(N ^ 2), N 作为正文的长度有道理的。对我而言,这感觉不正确:我可以看到必须进行O(N)调用,因为有N个后缀需要检查,但是,对于任何给定的前缀,我们永远不会考虑放置换行符(或者' split_point' )超出最大线长 L 。因此,对于任何给定的文本,最多有 L 位置来放置分割点(这假设最坏的情况:每个单词恰好是一个字符长)。由于这种实现,该算法不能更准确地描述为O(LN)?
@memoize
def justify(text, line_length):
# If the text is less than the line length, do not split
if len(' '.join(text)) < line_length:
return [], math.pow(line_length - len(' '.join(text)), 3)
best_cost, best_splits = sys.maxsize, []
# Iterate over text and consider putting split between each word
for split_point in range(1, len(text)):
length = len(' '.join(text[:split_point]))
# This split exceeded maximum line length: all future split points unacceptable
if length > line_length:
break
# Recursively compute the best split points of text after this point
future_splits, future_cost = justify(text[split_point:], line_length)
cost = math.pow(line_length - length, 3) + future_cost
if cost < best_cost:
best_cost = cost
best_splits = [split_point] + [split_point + n for n in future_splits]
return best_splits, best_cost
先谢谢你的帮助, 乙
答案 0 :(得分:3)
首先,您的实施将远远超出您想要的理论效率。您正在记住调用中长度为N
的字符串,这意味着查找数据的缓存副本可能是O(N)
。现在开始进行多次缓存调用,你已经破坏了复杂性预算。
这可以通过在函数调用之外移动文本并仅传递起始位置的索引和长度L
来解决。您还在循环内部进行O(L)
操作的连接。有些小心,你可以改为进行O(1)
操作。
完成后,您将进行O(N*L)
次操作。正是出于你想的原因。