最长公共子串(Longest Common Substring)与最长公共子序列(Longest Common Subsequence)的区别: 子串要求在原字符串中是连续的,而子序列则只需保持相对顺序一致,并不要求连续。
(1)例如X = {a, Q, 1, 1}; Y = {a, 1, 1, d, f}那么,{a, 1, 1}是X和Y的最长公共子序列,但不是它们的最长公共字串。
(2)S1 = ABCD,S2=AEBD,那么S1与S2的最长公共子序列是{ABD}。
S1[0...m]表示:S1字符串下标从0到m的子串。
S2[0...n]表示:S2字符串下标从0到n的子串。
那么最长公共子序列的状态转移方程为:
LCS(m,n)表示:S1[0...m]和S2[0...n]的最长公共子序列的长度。
(1)递归方法
class Solution:
def tryLCS(self, s1, s2):
if s1 == None or s2 == None:
return None
if len(s1) == 0 or len(s2) == 0:
return 0
# 求s1[0...m]和s2[0...n]的最长公共子序列的长度值
def LCS(s1, s2, m, n):
if m < 0 or n < 0:
return 0
if s1[m] == s2[n]:
return 1 + LCS(s1, s2, m-1, n-1)
else:
return max(LCS(s1, s2, m-1, n), LCS(s1, s2, m, n-1))
return LCS(s1, s2, len(s1)-1, len(s2)-1)
solution = Solution()
# test 1
s1 = "ABCDGH"
s2 = "ABDHG"
print(solution.tryLCS(s1, s2))
(2)记忆化搜索算法
class Solution:
def tryLCS(self, s1, s2):
if s1 == None or s2 == None:
return None
if len(s1) == 0 or len(s2) == 0:
return ""
memo = [[-1 for _ in range(len(s2))] for _ in range(len(s1)) ]
# 求s1[0...m]和s2[0...n]的最长公共子序列的长度值
def LCS(s1, s2, m, n):
if m < 0 or n < 0:
return 0
if memo[m][n] != -1:
return memo[m][n]
if s1[m] == s2[n]:
res = 1 + LCS(s1, s2, m-1, n-1)
else:
res = max(LCS(s1, s2, m-1, n), LCS(s1, s2, m, n-1))
memo[m][n] = res
return res
return LCS(s1, s2, len(s1)-1, len(s2)-1)
solution = Solution()
# test 1
# s1 = "ABCDGH"
# s2 = "ABDHG"
# print(solution.tryLCS(s1, s2))
# test 2
s1 = "AAACCGTGAGTTATTCGTTCTAGAA"
s2 = "CACCCCTAAGGTACCTTTGGTTC"
print(solution.tryLCS(s1, s2))
(3)动态规划方法
class Solution:
def LCS(self, s1, s2):
m = len(s1)
n = len(s2)
# 对memo的第0行和第0列进行初始化
memo = [[0 for _ in range(n)] for _ in range(m)]
# 初始化第0列
for i in range(n):
if s1[0] == s2[i]:
for k in range(i,n):
memo[0][k] = 1
break
# 初始化第0行
for i in range(m):
if s1[i] == s2[0]:
for k in range(i,m):
memo[k][0] = 1
break
# 动态规划过程
for i in range(1, m):
for j in range(1, n):
if s1[i] == s2[j]:
memo[i][j] = 1 + memo[i - 1][j - 1]
else:
memo[i][j] = max(memo[i - 1][j], memo[i][j - 1])
return memo[m-1][n-1]
solution = Solution()
# test 1
s1 = "ABCDGH"
s2 = "ABDHG"
print(solution.LCS(s1, s2))
地址:https://leetcode.com/problems/maximum-length-of-repeated-subarray/
反向求解出s1与s2的最长公共子序列,而不是求最长公共子序列的长度。
例题:输出最长公共子序列。
class Solution:
def LCS(self, s1, s2):
m = len(s1)
n = len(s2)
# 对memo的第0行和第0列进行初始化
memo = [[0 for _ in range(n)] for _ in range(m)]
# 初始化第0列
for i in range(n):
if s1[0] == s2[i]:
for k in range(i,n):
memo[0][k] = 1
break
# 初始化第0行
for i in range(m):
if s1[i] == s2[0]:
for k in range(i,m):
memo[k][0] = 1
break
# 动态规划过程
for i in range(1, m):
for j in range(1, n):
if s1[i] == s2[j]:
memo[i][j] = 1 + memo[i - 1][j - 1]
else:
memo[i][j] = max(memo[i - 1][j], memo[i][j - 1])
# return memo[m-1][n-1]
# 通过memo反向求解s1和s2的最长公共子序列
m = m - 1
n = n - 1
LCSstr = []
while m >= 0 and n >=0:
if s1[m] == s2[n]:
LCSstr.append(s1[m])
m = m - 1
n = n - 1
elif m == 0:
n = n - 1
elif n == 0:
m = m - 1
else:
if memo[m - 1][n] > memo[m][n - 1]:
m = m -1
else:
n = n - 1
return LCSstr
solution = Solution()
# test 1
s1 = "ABCDGH"
s2 = "ABDHG"
print(solution.LCS(s1, s2))
练习题(未完成):
【LeetCode】300. Longest Increasing Subsequence
要求:打印出最长上升子序列。
【动态规划】0-1背包问题
要求:输出背包中放的具体有哪些物品。