如果我有两个序列(例如,字符串)
// 01234567890123456789012
a = "AAACDDFFFEE1122VV1VAADD"
// 0123456789012345678901
b = "DDFFAA11221DHHVV1VAAFE"
我想知道从b到a的最佳子串匹配(无序),例如:
optimal (6 matched parts, 19 characters of a matched)
b a
DDFF -> DDFF (4,7)
AA -> AA (0,1)
1122 -> 1122 (11,14)
1
D -> D (21)
HH
VV1VAA -> VV1VAA (15,20)
FE -> FE (8,9)
还有另一种解决方案,但不是最佳解决方案:
not optimal (8 parts matched, 19 characters of a matched)
b a
DDFF -> DDFF (4,7)
AA -> AA (0,1)
1122 -> 1122 (11,14)
1 -> 1 (17)
D -> D (21)
HH
VV -> VV (15,16)
1
VAA -> VAA (18,20)
FE -> FE (8,9)
什么样的算法更适合这个问题? (我需要最佳结果,性能至关重要)。
感谢。
答案 0 :(得分:1)
有趣的问题,您可以使用Boyer-Moore(http://en.wikipedia.org/wiki/Boyer - Moore_string_search_algorithm)或KMP(http://en.wikipedia.org/wiki/Knuth - Morris在O(| a |。| b | + | b | ^ 2)中解决它-Pratt_algorithm)算法或任何其他线性时间字符串搜索算法。
总复杂度最多为总和(O(| a | + i),i = 0 .. | b |)= O(| a |。| b | + | b | ^ 2)但它可以很多如果只能在a中找到b的小子串,那就更小了。
编辑:
上述方法很贪婪,不会减少部件数量,但我认为它会最大化匹配的字符总数。
<小时/> 关于最佳解决方案的想法:
总和很容易计算,因为一个非常简单的解决方案是只匹配大小为1的子串:然后长度是a和b之间的公共字母数。
因此,如果我们将b的大小为1的子字符串(即使是不在a中的字母)添加到上面的匹配字符串集中,我们需要找到b的最小集合覆盖,而不会重叠。
一般的封面是NP完全的,但是这里有没有重叠的约束,它有帮助吗?
我正在研究它。
确实,NP-complete:http://www.springerlink.com/content/n73561q050w54pn6/
您可能想要寻找近似算法....
答案 1 :(得分:0)
如果我理解你的问题,你想要找到一组两个给定字符串的非重叠公共子串,这些子串最大化公共子串的总长度,并且最小化公共子串的数量。我将提出以下启发式:找到两个字符串中最长的公共子字符串(LCS),删除它,重复。我不能证明这是最优的,但我有一个非常有效的算法
所以在你的例子中 AAACDDFFFEE1122VV1VAADD DDFFAA11221DHHVV1VAAFE LCS = VV1VAA
AAACDDFFFEE1122DD DDFFAA11221DHHFE
LCS = DDFF
AAACFEE1122DD AA11221DHHFE
LCS = 1122
AAACFEEDD AADHHFE
等等
算法如下 1)使用基于后缀树的标准LCS算法 1.1构建连接的两个字符串的后缀树和唯一的终止符 1.2用1,2或两者标记每个节点,这取决于那里生根的子树是否有来自其中一个或两个字符串的叶子 1.3计算每个节点的字符串深度 1.4找到标记为1和2的字符串最深的节点 2)删除以该节点为根的子树,并更新其上方节点的标签 3)从1.4重复
当树没有标记为1和2的节点时,算法终止 1.1可以在与两个弦的长度之和成比例的时间内完成 1.2,1.3和1.4只是树遍历 2如果树实现正确并且更新受LCS长度的限制,则删除应该是恒定时间 3再次是树遍历但是树较小
所以这是一个优化,为了避免重复的树遍历,让我们添加步骤1.35:按字符串深度排序两个标签的内部节点(在单独的数据结构中,树仍在那里)。现在,您可以扫描排序的节点列表,执行2)并重复。通过这种优化,如果你可以使用基数排序,看起来算法是线性时间,你无法以渐近的方式击败它。
我希望这是正确和清晰的,我相信在听起来很明显之前你需要先熟悉一下后缀树文献。我推荐Dan Gusfield的书“字符串,树和序列算法”,特别是7.4节如果你有问题,请告诉我。