我正在尝试实现经典问题的解决方案,但我感到很困惑,因为我遇到了一些我找不到好结果的情况。
输出:“最长公共序列”,“长度”
def longestCommonSeq(x,y):
LCS = [[0 for z in range(len(y)+1)] for z in range(len(x)+1)]
for i in range(len(x)):
LCS[i][0]=0
for j in range(len(y)):
LCS[0][j]=0
for i in range(len(x)):
for j in range(len(y)):
if x[i]== y[j]:
LCS[i][j]= 1+LCS[i-1][j-1]
else:
LCS[i][j]= max(LCS[i-1][j],LCS[i][j-1])
i, j= len(x),len(y)
print LCS
res = ""
while i>0 and j>0:
if x[i-1]== y[j-1]:
res= x[i-1] + res
i-=1
j-=1
else:
if LCS[i-1][j]>LCS[i][j-1]:
i-=1
else:
j-=1
return res, LCS[len(x)-1][len(y)-1]
对于第一种情况:
x= "AGGTAB"
y= "GXTXAYB"
print longestCommonSeq(x,y)
结果: ('GTAB', 4)
这是正确的!
但是对于这样的情况:
a= "APBCADCQER"
b= "RASBTAUCVE"
print longestCommonSeq(a,b)
('', 5)
我错过了什么?,任何人都可以提示吗?
答案 0 :(得分:2)
你正在建造错误的桌子;您应该将行i = 0
和j = 0
处的行完全保留为0,并将匹配放入较高的行。这意味着您需要在第一个循环中将i
和j
坐标添加1,因为Python将索引设置为0。
结果,你最终得到一张最后有0的表;使用与explanation on Wikipedia类似的符号来构建它:
┌─┬─────┬─────────┬─────────┬─────┬──────┬───────┬─┐
│ │A │G │G │T │A │B │ │
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│G│< ^ 0│\ G │\ G │< G │< G │< G │0│
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│X│< ^ 0│^ G │< ^ G │< ^ G│< ^ G │< ^ G │0│
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│T│< ^ 0│^ G │< ^ G │\ GT │< GT │< GT │0│
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│X│< ^ 0│^ G │< ^ G │^ GT │< ^ GT│< ^ GT │0│
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│A│\ A │< ^ A | G│< ^ A | G│^ GT │\ GTA │< GTA │0│
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│I│^ A │< ^ A | G│< ^ A | G│^ GT │^ GTA │< ^ GTA│0│
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│B│^ A │< ^ A | G│< ^ A | G│^ GT │^ GTA │\ GTAB │0│
├─┼─────┼─────────┼─────────┼─────┼──────┼───────┼─┤
│ │0 │0 │0 │0 │0 │0 │0│
└─┴─────┴─────────┴─────────┴─────┴──────┴───────┴─┘
< and ^: maximum picked
\: add matching character to cell to the top left.
这会将您的上一列和行设置为0而不是第一列。幸运的是,Python将-1
解释为最后一个元素,因为在i = 0
或j = 0
这正是您最终要做的事情。你想要的是:
┌─┬─┬─────┬─────────┬─────────┬─────┬──────┬───────┐
│ │ │A │G │G │T │A │B │
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│ │0│0 │0 │0 │0 │0 │0 │
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│G│0│< ^ 0│\ G │\ G │< G │< G │< G │
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│X│0│< ^ 0│^ G │< ^ G │< ^ G│< ^ G │< ^ G │
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│T│0│< ^ 0│^ G │< ^ G │\ GT │< GT │< GT │
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│X│0│< ^ 0│^ G │< ^ G │^ GT │< ^ GT│< ^ GT │
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│A│0│\ A │< ^ A | G│< ^ A | G│^ GT │\ GTA │< GTA │
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│I│0│^ A │< ^ A | G│< ^ A | G│^ GT │^ GTA │< ^ GTA│
├─┼─┼─────┼─────────┼─────────┼─────┼──────┼───────┤
│B│0│^ A │< ^ A | G│< ^ A | G│^ GT │^ GTA │\ GTAB │
└─┴─┴─────┴─────────┴─────────┴─────┴──────┴───────┘
接下来,您不需要在第二个循环中咨询x
和y
,您只需 LCS
表。由于您最后得到0
,因此在最后一行和第一列中,当x
和y
的最后一个字符不匹配时,您的算法版本会停滞不前由于未能达到表中列出的LCS路径,因此几乎随机递减i
和j
。
请注意,前两个for
循环完全是冗余的,可以删除;您的整个LCS
表仅包含0
个值,为什么还要将它们设置为0
?维基百科中的伪代码使用从1到长度(包括)的循环,但是由于Python从0开始并且你在长度上加1以保持包含长度,所以你不需要在这里执行此操作。
您的更正后的功能如下:
def longestCommonSeq(x,y):
LCS = [[0 for z in range(len(y) + 1)] for z in range(len(x) + 1)]
for i in range(len(x)):
for j in range(len(y)):
if x[i] == y[j]:
LCS[i + 1][j + 1] = 1 + LCS[i][j]
else:
LCS[i + 1][j + 1] = max(LCS[i][j + 1], LCS[i + 1][j])
res = ''
i, j = len(x), len(y)
while i and j:
if LCS[i][j] == LCS[i-1][j]:
i -= 1
elif LCS[i][j] == LCS[i][j-1]:
j -= 1
else:
res = x[i-1] + res
i -= 1
j -= 1
return res, LCS[-1][-1]
由于LCS
表现已正确构建,因此我们也可以使用LCS[-1][-1]
作为最大长度值。
这会产生预期的结果:
>>> x = "AGGTAB"
>>> y = "GXTXAYB"
>>> print longestCommonSeq(x, y)
('GTAB', 4)
>>> a = "APBCADCQER"
>>> b = "RASBTAUCVE"
>>> print longestCommonSeq(a, b)
('ABACE', 5)
另一种方法是在第二阶段修复一个一个,并查看实际对应输入字的LCS表格单元格;例如从i
和j
减去1:
def longestCommonSeq(x,y):
LCS = [[0 for z in range(len(y)+1)] for z in range(len(x)+1)]
for i in range(len(x)):
for j in range(len(y)):
if x[i] == y[j]:
LCS[i][j] = 1 + LCS[i - 1][j - 1]
else:
LCS[i][j] = max(LCS[i - 1][j], LCS[i][j - 1])
i, j = len(x) - 1, len(y) - 1
print LCS
res = ""
while i >= 0 and j >= 0:
if x[i] == y[j]:
res= x[i] + res
i -= 1
j -= 1
else:
if LCS[i - 1][j] > LCS[i][j - 1]:
i -= 1
else:
j -= 1
return res, LCS[len(x) - 1][len(y) - 1]
现在i
和j
的范围分别从len(x) - 1
到0
和len(y) - 1
到0
,您再次引用正确的单元格