📅  最后修改于: 2023-12-03 14:48:53.038000             🧑  作者: Mango
最长公共子序列(Longest Common Subsequence,简称 LCS)指的是在两个序列中分别找到一个最长的公共子序列。
比如,对于序列 A={1,3,4,5,6,7,7,8} 和序列 B={3,5,7,4,8,6,7,8,2},它们的最长公共子序列为{3,4,6,7,8}。
常见的求解最长公共子序列问题的方法有动态规划和贪心算法。
动态规划是一种解决最长公共子序列问题的常用方法。这种方法的思路是:将原问题划分成若干子问题,递归求解子问题,再将子问题的解组合起来得到原问题的解。
由于最长公共子序列的长度不一定是原序列的长度,因此可以这样设计状态:
设 $c[i][j]$ 表示序列 $A$ 的前 $i$ 个元素和序列 $B$ 的前 $j$ 个元素的最长公共子序列长度。
因此,若 $A[i]=B[j]$,则 $c[i][j]=c[i-1][j-1]+1$,否则 $c[i][j]=\max(c[i-1][j],c[i][j-1])$。
最后,$c[m][n]$ 即为序列 $A$ 和序列 $B$ 的最长公共子序列长度。
以下是基于动态规划的求解最长公共子序列问题的 Python 代码实现:
def lcs(arr1, arr2):
m, n = len(arr1), len(arr2)
# 初始化状态数组
c = [[0] * (n+1) for _ in range(m+1)]
# 动态规划求解
for i in range(1, m+1):
for j in range(1, n+1):
if arr1[i-1] == arr2[j-1]:
c[i][j] = c[i-1][j-1] + 1
else:
c[i][j] = max(c[i-1][j], c[i][j-1])
# 反向查找最长公共子序列
i, j = m, n
result = []
while i > 0 and j > 0:
if arr1[i-1] == arr2[j-1]:
result.insert(0, arr2[j-1])
i -= 1
j -= 1
elif c[i-1][j] > c[i][j-1]:
i -= 1
else:
j -= 1
return result
贪心算法通过一系列局部最优的选择,最终得到全局最优的解。对于最长公共子序列问题,可以设计这样的贪心策略:从左至右依次考虑序列 $A$,对于 $A$ 中的每个元素,看它是否在序列 $B$ 中出现,若出现则将其记录至最长公共子序列中。
以下是基于贪心算法的求解最长公共子序列问题的 Python 代码实现:
def lcs(arr1, arr2):
result = []
# 用集合存储 arr2 的元素,方便查找
s = set(arr2)
for x in arr1:
if x in s:
result.append(x)
# 删除已选择的元素,避免重复选择
s.remove(x)
return result
对于一个仅由不同元素组成的数组,我们可以使用贪心策略来求解其最长公共子序列。由于相同元素在两个数组中都只出现一次,我们可以将每个元素看作一条边,构建出两个元素组成的图,然后找到两个数组之间的最长路径,即为所求的最长公共子序列。
以下是基于贪心算法的求解仅由不同元素组成的数组的最长公共子序列问题的 Python 代码实现:
def lcs(arr1, arr2):
result = []
# 将元素看作边,构建图
graph1 = dict(zip(arr1, arr1[1:]))
graph2 = dict(zip(arr2, arr2[1:]))
# 找到两个数组之间的最长路径
path = []
for v in graph1:
if v in graph2:
visited = set()
queue = [(v, [v])]
while queue:
(vertex, path) = queue.pop(0)
if vertex not in visited:
visited.add(vertex)
for neighbor in (graph1.get(vertex, []) + graph2.get(vertex, [])):
queue.append((neighbor, path + [neighbor]))
if neighbor in graph2:
if len(path) > len(result):
result = path
graph2.pop(vertex, None)
return result
以上是两种求解两个数组的最长公共子序列的方法,根据具体问题进行选择即可。