最长公共子串
问题描述: 给定两个不为空的字符串,求这两个字符串之间最长的公共子串。
思路: 我们借助动态规划的思想,利用递推公式起始向后逐步计算两个字符串的部分最长公共子串,直至得出整体的最大子串。递推公式如下:
f
(
i
,
j
)
=
{
0
A
[
i
]
≠
B
[
j
]
∩
(
i
=
0
∪
j
=
0
)
1
A
[
i
]
=
B
[
j
]
∩
(
i
=
0
∪
j
=
0
)
0
A
[
i
]
≠
B
[
j
]
f
(
i
−
1
,
j
−
1
)
+
1
A
[
i
]
=
B
[
j
]
f(i,j)=\begin{cases} 0 & \text A[i]\not=B[j]\cap (i=0 \ \cup \ j = 0)\\1 & \text A[i]=B[j]\cap (i=0 \ \cup \ j = 0)\\0 & \text A[i]\not=B[j]\\ f(i-1, j-1) +1 & \text A[i]=B[j] \end{cases}
f(i,j)=⎩⎪⎪⎪⎨⎪⎪⎪⎧010f(i−1,j−1)+1A[i]=B[j]∩(i=0 ∪ j=0)A[i]=B[j]∩(i=0 ∪ j=0)A[i]=B[j]A[i]=B[j]
同levenshtein编辑距离算法类似,最长公共子串同样需要填充表格来枚举所有位置的编辑距离,从而找出最大值。下面我们给出实现代码:
class solution:
def __init__(self, A, B):
self.A = A
self.B = B
self.Matrix = []
self.lcstring(self.A, self.B)
def lcstring(self, A, B):
lengthA = len(A)
lengthB = len(B)
longest = 0
longestend = []
if lengthA == 0 or lengthB == 0 :
return
for i in range(lengthA):
self.Matrix.append([])
for j in range(lengthB):
if A[i] == B[j] :
if i == 0 or j == 0:
self.Matrix[i].append(1)
else: self.Matrix[i].append(self.Matrix[i-1][j-1]+1)
if self.Matrix[i][j] > longest:
longestend.clear()
longest = self.Matrix[i][j]
longestend.append(i)
continue
if self.Matrix[i][j] == longest:
longestend.append(i)
else: self.Matrix[i].append(0)
print(longest)
for i in longestend:
print(A[i-(longest-1):i+1])
if __name__ == '__main__':
sol = solution("helloworld","loop")
上述代码输出为最长公共子串的长度,以及该长度的所有子串集。运行结果如下:
我们将输入字符串稍作修改(”helloworld“改为“hilloworld”,“loop”改为“iloor”),以测试其能否完全输出所有子串,输出结果如下:
输出结果表明,我们的算法基本正确,读者可以尝试使用。