我试图找到两个字符串之间最长的公共子序列。
我看了这个tutoial Python's wget
并写道:
# Longest Common Subsequence
def lcs(s1, s2):
matrix = [ [0 for x in range(len(s2))] for x in range(len(s1)) ]
cs = ""
for i in range(len(s1)):
for j in range(len(s2)):
if s1[i]==s2[j]:
if i==0 or j==0:
matrix[i][j] = 1
cs += s1[i]
else:
matrix[i][j] = matrix[i-1][j-1] + 1
cs += s1[i]
else:
if i==0 or j==0:
matrix[i][j] = 0
else:
matrix[i][j] = max(matrix[i-1][j], matrix[i][j-1])
return matrix[len(s1)-1][len(s2)-1], cs
print(lcs("abcdaf", "acbcf"))
I get (3, 'abccaf')
这显然是错误的应该是4 abcf。
不确定哪一步出错了。一个普遍的问题是程序员通常需要多长时间才能“获得”这类问题?
答案 0 :(得分:3)
您的代码有两个主要问题导致算法输出错误的答案。
第16行if i == 0 or j == 0
在视频后面显示此行在s1[1] != s2[j]
时无效,因为“ab”和“a”的最长公共子序列的长度为1,尽管您的算法为此示例设置了matrix[0][1] = 0
。所以你需要删除这个if语句。在你的时候,你必须考虑max(matrix[i-1][j], matrix[i][j-1])
对i == 0
或j == 0
的作用。现在有两种不同的方法:
明确的一个:
max(matrix[i-1][j] if i != 0 else 0,
matrix[i][j-1] if j != 0 else 0)
隐含的一个:
max(matrix[i-1][j], matrix[i][j-1])
这个有效,因为在Python中,负数索引用于获取列表的最后一项,在这种情况下这些项为0。
cs += s1[i]
例如,如果您发现“a”和“abcd”的最长公共子序列是“a”,则您的算法将“a”和“abcda”的最长公共子序列设置为“aa”,而不是合理。我正在努力解释为什么它不起作用,所以我建议你看几个例子,也许使用http://pythontutor.com/visualize.html
要解决这两个问题,您可以使用矩阵存储您为较小问题找到的最长公共子序列。你最终得到了这个:
def lcs(s1, s2):
matrix = [["" for x in range(len(s2))] for x in range(len(s1))]
for i in range(len(s1)):
for j in range(len(s2)):
if s1[i] == s2[j]:
if i == 0 or j == 0:
matrix[i][j] = s1[i]
else:
matrix[i][j] = matrix[i-1][j-1] + s1[i]
else:
matrix[i][j] = max(matrix[i-1][j], matrix[i][j-1], key=len)
cs = matrix[-1][-1]
return len(cs), cs
print(lcs("abcdaf", "acbcf"))
此特定实现仅返回一个可能的结果。您可以尝试实现一种算法,该算法将所有最长的常见序列作为练习。也许看看גלעדברקן
建议的Wikipedia page显然没有明确的答案。考虑示例总是有帮助的,并且在算法的情况下,维基百科通常具有良好的伪代码,您可以将其实现为基础。当您熟悉算法中涉及的概念和数据结构时,您应该能够在一天之内实现它,我想说(但我绝对不是专家)。通常,在代码中搜索逻辑错误可能需要多天,具体取决于代码的大小。为了练习这种结构化,算法和数学思维,我强烈推荐projecteuler.net。
答案 1 :(得分:1)
对于那些寻求内置解决方案的人:
from difflib import SequenceMatcher
str_a = "xBCDxFGxxxKLMx"
str_b = "aBCDeFGhijKLMn"
s = SequenceMatcher(None, str_a, str_b)
lcs = ''.join([str_a[block.a:(block.a + block.size)] for block in s.get_matching_blocks()])
# lcs = 'BCDFGKLM'
答案 2 :(得分:0)
上一个答案不是正确的解决方案(即使在大多数情况下也可以使用),正确的解决方案是创建矩阵,然后回溯矩阵 这是先前答案失败的情况 “ AAACCGTGAGTTATTCGTTCTAGAA” “ CACCCCTAAGGTACCTTTGGTTC” 最长的公共子序列应为“ ACCTAGTACTTTG”,但先前的代码未返回正确的result。
(以下代码使用python 3)
def my_lcs(x_string, y_string):
matrix = [[0 for each_x in range(0,len(y_string)+1)] for each_y in range(0,len(x_string)+1)]
for each_y in range(len(y_string)):
for each_x in range(len(x_string)):
prev_x =each_x-1
prev_y =each_y-1
if(x_string[prev_x]== y_string[prev_y]):
matrix[each_x][each_y] = matrix[prev_x][prev_y] + 1
else:
matrix[each_x][each_y] = max(matrix[prev_x][each_y] , matrix[each_x][prev_y])
return matrix
#print /backtrack
def print_lcs( mtrx, x_string, y_string ):
result = []
x, y = len( x_string ), len(y_string)
while x> 0 and y > 0:
if x_string[x- 1] == y_string[y - 1]:
result.append( x_string[x- 1] )
x-= 1
y -= 1
elif mtrx[x][y - 1] >= mtrx[x- 1][y]:
y -= 1
else:
x-= 1
print(result[::-1] )
inputa, inputb="1ab", "2ab" #ab
#inputa, inputb="AAACCGTGAGTTATTCGTTCTAGAA", "CACCCCTAAGGTACCTTTGGTTC" #ACCTAGTACTTTG
#inputa, inputb="houseboat", "computer"#oue
#inputa, inputb="2193588", "21943588" #2193588
#inputa, inputb="ABCBDAB", "BDCABA" #BDAB
result= my_lcs(inputa, inputb)
print_lcs(result,inputa, inputb)
这是基于https://www.cs.fsu.edu/~burmeste/slideshow/chapter16/16-3.html和http://www.columbia.edu/~cs2035/courses/csor4231.F11/lcs.pdf
答案 3 :(得分:0)
通过使用以下代码段获取LCS的长度,您会发现最大长度为14,因此BurningKarl的解决方案对我有用。
def longestCommonSequence(s1, s2, s1Index, s2Index, arr):
if s1Index ==-1 or s2Index == -1:
return 0
if(arr[s1Index][s2Index] != None):
return arr[s1Index][s2Index]
if s1[s1Index] == s2 [s2Index]:
result = 1+ longestCommonSequence(s1, s2, s1Index -1, s2Index -1, arr)
else:
result= max(longestCommonSequence(s1, s2, s1Index -1, s2Index, arr), longestCommonSequence(s1, s2, s1Index, s2Index -1, arr))
arr[s1Index][s2Index] = result
return result
s1="AAACCGTGAGTTATTCGTTCTAGAA"
s2="CACCCCTAAGGTACCTTTGGTTC"
a = [[None for i in range(len(s2))] for j in range(len(s1))]
print(longestCommonSequence(s1, s2, len(s1)-1, len(s2)-1, a))
答案 4 :(得分:0)
def subrange(i, strr) :
a = i[len(i) - 1]
for j in range(len(i) - 1, len(strr)) :
if a == strr[j] :
return j
return
def subseq(strr) :
prev = [i for i in strr]
nex1 = []
nex = []
a = set()
for i in prev :
a.add(i)
while( len(prev[0])< len(strr)):
for i in prev :
for j in range(subrange(i,strr) + 1, len(strr)) :
nex.append(i + strr[j])
prev = nex
nex = []
for i in prev :
a.add(i)
return a
a1 = []
a2 = []
sol = ""
maxx = 0
str1 = input()
str2 = "sai"
if(len(str1) == 0 or len(str2) == 0) :
sol = "NULL"
else :
a1 = list(subseq(str1))
a2 = list(subseq(str2))
for i in range(0, len(a1)) :
for j in range(0, len(a2)) :
if a1[i] == a2[j] :
if len(a1[i]) > maxx :
sol = a1[i]
maxx = len(sol)
print(sol)