数据结构与算法之 leetcode 动态规划最长公共子序列算法 DP 表

数据结构与算法阿木 发布于 3 天前 2 次阅读


动态规划求解最长公共子序列(LCS)算法详解

动态规划(Dynamic Programming,简称DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学等领域中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。在算法设计中,动态规划是一种非常有效的技术,它能够将复杂问题分解为一系列简单的子问题,并存储这些子问题的解,以避免重复计算。

最长公共子序列(Longest Common Subsequence,简称LCS)问题是动态规划中一个经典的问题。它指的是两个序列中,能够以任意顺序出现的最长序列。例如,序列“AGGTAB”和“GXTXAYB”的最长公共子序列是“GTAB”。

本文将围绕动态规划求解最长公共子序列算法(DP 表)这一主题,详细讲解算法的原理、实现以及优化。

算法原理

LCS问题可以通过动态规划的方法来解决。动态规划的核心思想是将问题分解为子问题,并存储子问题的解,以避免重复计算。

子问题定义

假设有两个序列A和B,长度分别为m和n。我们可以定义一个子问题LCS(i, j),表示序列A的前i个字符和序列B的前j个字符的最长公共子序列的长度。

状态转移方程

根据子问题的定义,我们可以得到以下状态转移方程:

- 如果A[i-1] == B[j-1],则LCS(i, j) = LCS(i-1, j-1) + 1。

- 如果A[i-1] != B[j-1],则LCS(i, j) = max(LCS(i-1, j), LCS(i, j-1))。

DP表

为了存储子问题的解,我们可以使用一个二维数组DP,其中DP[i][j]表示LCS(i, j)的值。

代码实现

下面是使用Python实现LCS算法的代码:

python

def lcs(A, B):


m, n = len(A), len(B)


初始化DP表


DP = [[0] (n + 1) for _ in range(m + 1)]

填充DP表


for i in range(1, m + 1):


for j in range(1, n + 1):


if A[i - 1] == B[j - 1]:


DP[i][j] = DP[i - 1][j - 1] + 1


else:


DP[i][j] = max(DP[i - 1][j], DP[i][j - 1])

返回最长公共子序列的长度


return DP[m][n]

测试代码


A = "AGGTAB"


B = "GXTXAYB"


print("最长公共子序列的长度为:", lcs(A, B))


优化

在实际应用中,我们可以对LCS算法进行一些优化,以提高算法的效率。

空间优化

在上述代码中,我们使用了一个二维数组DP来存储子问题的解。由于状态转移方程只依赖于上一行的数据,因此我们可以将DP表的空间复杂度从O(mn)优化到O(min(m, n))。

python

def lcs_optimized(A, B):


m, n = len(A), len(B)


if m < n:


A, B = B, A


初始化DP表


DP = [0] (n + 1)

填充DP表


for i in range(1, m + 1):


temp = DP[:]


for j in range(1, n + 1):


if A[i - 1] == B[j - 1]:


temp[j] = temp[j - 1] + 1


else:


temp[j] = max(temp[j], DP[j])


DP = temp

返回最长公共子序列的长度


return DP[n]

测试代码


print("最长公共子序列的长度为:", lcs_optimized(A, B))


时间优化

在上述代码中,我们使用了一个二维数组DP来存储子问题的解。我们可以通过只存储当前行和上一行的数据来进一步优化算法的时间复杂度。

python

def lcs_time_optimized(A, B):


m, n = len(A), len(B)


if m < n:


A, B = B, A


初始化DP表


DP = [0] (n + 1)

填充DP表


for i in range(1, m + 1):


for j in range(1, n + 1):


if A[i - 1] == B[j - 1]:


DP[j] = DP[j - 1] + 1


else:


DP[j] = max(DP[j], DP[j - 1])

返回最长公共子序列的长度


return DP[n]

测试代码


print("最长公共子序列的长度为:", lcs_time_optimized(A, B))


总结

本文详细介绍了动态规划求解最长公共子序列(LCS)算法的原理、实现以及优化。通过动态规划,我们可以有效地解决LCS问题,并提高算法的效率。在实际应用中,我们可以根据具体需求对算法进行优化,以达到更好的性能。