我有一个多项式时间算法,我懒得写代码。但我会为你描述它。
首先,使集合中的每个字符串加上空字符串成为图的节点。空字符串与其他字符串相连,反之亦然。如果一个字符串的结尾与另一个字符串的开头重叠,它们也会连接。如果两个可以重叠不同的数量,它们就会得到多个边缘。 (所以它不完全是一个图表......)
每条边都有一个成本和一个值。 cost 是您必须扩展您正在构建的字符串以从旧端移动到新端的字符数。 (换句话说,第二个字符串的长度减去重叠的长度。)拥有这个。 值是你完成了多少个新字符串跨越了前一个字符串和后一个字符串之间的障碍。
您的示例是 {"ab", "bd", "abd" "babd", "abc"}。以下是每个转换的 (cost, value) 对。
from -> to : (value, cost)
"" -> "ab": ( 1, 2)
"" -> "bd": ( 1, 2)
"" -> "abd": ( 3, 3) # we added "ab", "bd" and "abd"
"" -> "babd": ( 4, 4) # we get "ab", "bd", "abd" and "babd"
"" -> "abc": ( 2, 3) # we get "ab" and "abc"
"ab" -> "": ( 0, 0)
"ab" -> "bd": ( 2, 1) # we added "abd" and "bd" for 1 character
"ab" -> "abd": ( 2, 1) # ditto
"ab" -> "abc": ( 1, 1) # we only added "abc"
"bd" -> "": ( 0, 0) # only empty, nothing else starts "bd"
"abd" -> "": ( 0, 0)
"babd" -> "": ( 0, 0)
"babd" -> "abd": ( 0, 0) # 重叠,但没有添加任何内容。
"abc" -> "": (0, 0)
好的,所有这些都设置好了。我们为什么要这张图?
请注意,如果我们从成本为 0 且值为 0 的 "" 开始,然后通过图的路径构建一个字符串。它正确地说明了成本,并提供了价值的下限。该值可以更高。例如,如果你的集合是 {"ab", "bc", "cd", "abcd"} 那么路径 "" -> "ab" -> "bc" -> "cd" 将导致字符串 "abcd " 成本为 4,预测值为 3。但该值估计忽略了我们匹配“abcd”这一事实。
但是,对于仅由集合中的子字符串组成的任何给定字符串,有一条通过图的路径具有正确的成本和正确的值。 (在每个选择中,您都希望选择尚未计算的最早的起始匹配字符串,并从中选择最长的字符串。这样您就不会错过任何匹配项。)
因此,我们将问题从构造字符串转向了通过图构造路径。我们要做的是建立以下数据结构:
for each (value, node) combination:
(best cost, previous node, previous value)
填充该数据结构是一个动态规划问题。一旦填写完毕,我们就可以通过它追溯找到图表中的哪条路径以该成本使我们达到该值。给定该路径,我们可以找出执行此操作的字符串。
它有多快?如果我们的集合有K字符串,那么我们只需要填写K * N值,每个值我们最多可以给K候选新值。这使得查找路径成为O(K^2 * N) 问题。