您的位置:首页 > 其它

动态规划求解最长公共子串与公共子序列

2018-01-20 18:24 483 查看
最长公共子序列(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))))


运行结果如下。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: