最长公共子序列(longest common sequence)和最长公共子串(longest common substring)是两个不同的问题。一个给定的序列的子序列,就是将给定序列中零个或多个元素去掉之后得到的结果。什么是子串呢?给定串中任意个连续的字符组成的子序列称为该串的子串。
(一)最长公共子序列:求得'ABCD'与'EFGACXD'最长公共子序列,结果为3。
假设动态规划中res[i][j]的值表示list1[i]与list2[j]为同尾时,最长的公共子序列长度。如果list1[i]与list2[j]相同,则res[i][j]=res[i-1][j-1] + 1,否则max(res[i-1][j], res[i][j-1])。
为了便于首行与首列的数据生成,则res的二维数组初始化为len1+1行,len2+1列,这样i+1,j+1位置的数据表示的是list1[i]与list2[j]对齐时候的最长公共子序列长度。
def longest_common_subsequence(list1, list2):
len1 = len(list1)
len2 = len(list2)
if len1 <=0 or len2 <= 0:
return 0
res = [[0 for i in range(len2+1)] for i in range(len1+1)]
for i in range(len1):
for j in range(len2):
if list1[i] == list2[j]:
res[i+1][j+1] = res[i][j] + 1
else:
res[i+1][j+1] = max(res[i][j+1], res[i+1][j])
return res[-1][-1]
list1 = 'ABCD'
list2 = 'EFGACXD'
print(longest_common_subsequence(list(list1), list(list2)))
(二)最长公共子串:给定两个字符串,求出它们之间最长的相同子字符串的长度。
设置一个二维矩阵,行数为len(list1),列数为len(list2),每个res[i][j]表示list1的i位置与list2的j位置对齐时候,这个位置之前的最长子串长度。显然,如果list1[i] != list2[j], res[i][j] = 0, 如果list1[i+1] = list2[j+1],那么res[i+1][j+1] = res[i][j] + 1,否则res[i+1][j+1] = 0。这个二维矩阵是沿-45度方向逐步生成的。
优化思路:由于是按行推导过来的,可以只用两个list保存i行与i+1行,滚动覆盖,这样能节省一定存储空间。第0行为初始化的,当做偶数行,之后每行先判断是奇数行还是偶数行,如果是奇数行,先清空,再用偶数行的值计算;如果是偶数行,先清空,用奇数行的值计算。
def longest_common_substring(list1, list2):
if len(list1) == 0 or len(list2) == 0:
return (0, 0, 0)#返回最大长度,begin位置,end位置
len1 = len(list1)
len2 = len(list2)
longest = 0
position = []
res = [[0 for i in range(len2+1)] for i in range(len1+1)]
for i in range(len1):
for j in range(len2):
if list1[i] == list2[j]:
res[i+1][j+1] = res[i][j] + 1
if res[i+1][j+1] > longest:
longest = res[i+1][j+1]
position = [(longest, i-longest+1, j-longest+1)]
elif res[i+1][j+1] == longest:
position.append((longest, i-longest+1, j-longest+1))
else:
res[i+1][j+1] = 0
return position
list1 = 'ABC'
list2 = 'EFG'
print('%s---%s, result:%s\n'%(list1, list2, longest_common_substring(list(list1), list(list2))))
list1 = 'ABC'
list2 = 'EFGA'
print('%s---%s, result:%s\n'%(list1, list2, longest_common_substring(list(list1), list(list2))))
list1 = 'ABCABC'
list2 = 'ABXYYAB'
print('%s---%s, result:%s\n'%(list1, list2, longest_common_substring(list(list1), list(list2))))
list1 = 'ABCDEF'
list2 = 'EFABCD'
print('%s---%s, result:%s\n'%(list1, list2, longest_common_substring(list(list1), list(list2))))
运行结果如下。