数据结构与算法之动态规划 最长子序列 LIS/LCS 问题解析

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


动态规划:最长子序列(LIS/LCS)问题解析

动态规划是一种在数学、管理科学、计算机科学、经济学和生物信息学等领域中广泛使用的方法。它通过将复杂问题分解为更小的子问题,并存储这些子问题的解以避免重复计算,从而提高算法的效率。本文将围绕动态规划中的两个经典问题——最长递增子序列(Longest Increasing Subsequence, LIS)和最长公共子序列(Longest Common Subsequence, LCS)——进行解析,并给出相应的代码实现。

最长递增子序列(LIS)

问题定义

给定一个无序数组 `nums`,返回其最长递增子序列的长度。

动态规划思路

我们可以使用一个辅助数组 `dp` 来存储以每个位置结尾的最长递增子序列的长度。对于数组中的每个元素 `nums[i]`,我们遍历之前的所有元素 `nums[j]`(其中 `j < i`),如果 `nums[j] < nums[i]`,则 `nums[i]` 可以作为 `nums[j]` 的下一个元素,此时 `dp[i]` 的值可以更新为 `dp[j] + 1`。最终,`dp` 数组中的最大值即为最长递增子序列的长度。

代码实现

python

def lengthOfLIS(nums):


if not nums:


return 0

dp = [1] len(nums)


for i in range(1, len(nums)):


for j in range(i):


if nums[i] > nums[j]:


dp[i] = max(dp[i], dp[j] + 1)


return max(dp)

示例


nums = [10, 9, 2, 5, 3, 7, 101, 18]


print(lengthOfLIS(nums)) 输出: 4


最长公共子序列(LCS)

问题定义

给定两个字符串 `text1` 和 `text2`,返回它们的最长公共子序列的长度。

动态规划思路

我们可以使用一个二维数组 `dp` 来存储两个字符串的公共子序列的长度。`dp[i][j]` 表示 `text1` 的前 `i` 个字符和 `text2` 的前 `j` 个字符的最长公共子序列的长度。如果 `text1[i-1] == text2[j-1]`,则 `dp[i][j] = dp[i-1][j-1] + 1`;否则,`dp[i][j] = max(dp[i-1][j], dp[i][j-1])`。

代码实现

python

def longestCommonSubsequence(text1, text2):


m, n = len(text1), len(text2)


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

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


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


if text1[i - 1] == text2[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]

示例


text1 = "abcde"


text2 = "ace"


print(longestCommonSubsequence(text1, text2)) 输出: 3


总结

本文介绍了动态规划在解决最长递增子序列(LIS)和最长公共子序列(LCS)问题中的应用。通过将问题分解为更小的子问题,并存储这些子问题的解,我们可以有效地解决这些问题。在实际应用中,动态规划是一种非常实用的算法设计方法,能够帮助我们解决许多复杂问题。

扩展

1. 最长递增子序列的优化:可以使用二分查找来优化 LIS 的解法,将时间复杂度降低到 O(n log n)。

2. 最长公共子序列的优化:可以使用空间优化技巧,将二维数组 `dp` 优化为一维数组,从而降低空间复杂度。

3. LCS 的应用:LCS 在生物信息学中有着广泛的应用,例如基因序列比对、蛋白质结构预测等。

通过本文的学习,相信读者对动态规划在解决 LIS 和 LCS 问题中的应用有了更深入的了解。希望这些知识能够帮助读者在未来的学习和工作中更好地应用动态规划。