问题1:给定两个字符串,求其最大公共子序列
例如asbcdfg和scdfgjkl, 则返回scdfg
使用动态规划求解,
假设s1=<x1,x2....xn> s2=<y1,y2..ym>
令f[i][j]表示串s1以索引i结尾,串s2以索引j结尾的最长公共子序列的长度。
当i==0或者j==0时,此时s1[i] s2[j]均为空,即有f[i][j]=0
i>=1,j>=1
当s1[i]==s2[j]时,有f[i][j] = f[i-1][j-1] + 1
当s1[i]!=s2[j]时,有f[i][j] = max(f[i-1][j], f[i][j-1])
由于字符串s索引从0开始,为了便于计算,描述如下:
f[i][j]表示s1已i结尾,s2已j结尾的最大公共子序列:
i, j=0 f[i][j]=0
当s1[i]==s2[j]时,有f[i+1][j+1] = f[i][j] + 1
当s1[i]!=s2[j]时,有f[i+1][j+1] = max(f[i][j-1], f[i-1][j])
def testl():
s1 = 'sabc'
s2 = 'abcs'
s = lcs(s1, s2)
print (s)
def lcs(s1, s2):
dp = [[0 for i in range(len(s1)+1)] for i in range(len(s2)+1)]
for i in range(0, len(s1)):
for j in range(0, len(s2)):
if s1[i]==s2[j]:
dp[i+1][j+1] = dp[i][j] + 1
else:
dp[i+1][j+1] = max(dp[i][j+1],dp[i+1][j])
print (mat(dp))
i = len(s1)
j = len(s2)
r = []
while i>0 and j>0:
#print (i,j,s1[i-1],s2[j-1])
if s1[i-1]==s2[j-1]:
r.append(s1[i-1])
i -= 1
j -= 1
elif dp[i-1][j]==dp[i][j]:
i -= 1
else:
j -= 1
r.reverse()
return r
动态规划数组如下所示: 表示0sabc 与0abcs的匹配关系:
现在找出最大的匹配子序列,
首先最大子序列长度值为dp[len(s1)][len(s2)],此时
若s1[i-1]==s2[j-1],即说明该字符即为公共字符,由于f[i][j] = f[i-1][j-1] + 1 可以沿对角斜向上寻找第二个字符 i-- j--
若两者不相等,则找出dp[i][j]的来源,由于f[i+1][j+1] = max(f[i][j-1], f[i-1][j]),要么左边,要么上边,若dp[i-1][j]==dp[i][j]说明来自上面,
则i--,否则j--
最后倒序输出序列即可。
问题2:给定两个字符串,求其最大公共子串
例如asbcdfg和scdfgjkl, 则返回cdfg 即必须是连续子串
s1=<x1,x2,,xm>
s2=<y1,y2...yn>
令f[i][j]表示s1以i结尾,s2以j结尾的最大公共子串:
i==0 j==0 f[i][j]=0
当s1[i]==s2[j]时,有f[i+1][j+1]=f[i][j]+1
当s1[i]!=s2[i]时,有f[i][j]=0
def lcs2(s1, s2):
dp = [[0 for i in range(len(s1)+1)] for i in range(len(s2)+1)]
row = 0
col = 0
max = 0
for i in range(0, len(s1)):
for j in range(0, len(s2)):
if s1[i]==s2[j]:
dp[i+1][j+1] = dp[i][j] + 1
if(dp[i][j] + 1>max):
max = dp[i][j] + 1
row = i
col = j
else:
dp[i+1][j+1] = 0
print (mat(dp))
return s1[i-max+1:i+1]
利用一个变量max来记录最大子串长度,利用row记录最大公共子串的最后一个字符在s1中的位置,返回最大公共子串:s1[i-max+1:i+1]