问题描述1
求两字符串的连续最大公共子字符串(The Longest Common Substring)
这个LCS问题就是求两个字符串最长公共子串的问题。解法就是用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0。然后求出对角线最长的1的序列,其对应的位置就是最长匹配子串的位置。如图1所示,在对角线上,连续的1就代表了两字符串对应的位置连续相等。
从该矩阵中找到最长的全为1的对角线,就可以找到最大公共子串,分别找出对应的字符即可求出这个最长子串。如图1,有两个最长的公共子串,bab与aba。
综上所述,基本思路分为两步:
- 算一个匹配矩阵存放两个字符串每个字符之间的匹配情况,相同为1,不同为0。
- 求这个矩阵中连续的最长的值为1的对角线,得到这个对角线之后我们就得到了公共子串的长度和位置(行列索引值)
改进算法
求出字符串匹配矩阵是很简单的事情,但是当我们得到0/1矩阵时,我们要怎么去找到最长的对角线呢?这是一个比较麻烦的问题。
我们可以对这个算法进行改进,即在填充这个矩阵的值的时候,不仅仅是0和1,而是把公共子串的最大长度的值填入相应的位置。我们的目的很清楚,就是在图1的基础上把最长的对角线的值改为1.2.3…,代表了公共子串的长度,看下面两个图会比较清楚:
我们想一下,把矩阵变成这样之后,每个元素的值m[i][j]
代表的是子串s1
的前i部分和子串s2
的前j部分中最大公共子串的长度。那么其实这个矩阵中最大的元素的值就代表了公共子串的最大长度,而且通过这个最大元素的行列索引我们就可以定位到这个公共子串在原字符串中的结束位置,再通过长度就可以反推出公共子串的内容了。下面我们讲一下代码思路:
思路:当两个字符串中找到匹配的字符之后,s1[i]=s2[j]
,那么我们要填入矩阵[i,j]位置的值取决于它的左上方的值,填入的值为左上方的值+1,代表这个字符可以加入现有的公共子串。
代码
def find_lcsubstr(s1, s2):
m = [[0 for i in range(len(s2) + 1)] for j in range(len(s1) + 1)] # 生成0矩阵,为方便后续计算,比字符串长度多了一列
mmax = 0 # 最长匹配的长度
p = 0 # 最长匹配对应在s1中的最后一位
for i in range(len(s1)):
for j in range(len(s2)):