📜  最长公共子序列 | DP 使用记忆化(1)

📅  最后修改于: 2023-12-03 15:40:16.970000             🧑  作者: Mango

最长公共子序列 | DP 使用记忆化

简介

最长公共子序列(Longest Common Subsequence,简称 LCS)是指两个序列中的最长公共子序列。

  • 序列:一个序列是一个有序列表,可以用数组表示;
  • 子序列:一个序列通过去掉一些数得到的序列;
  • 公共子序列:两个序列都含有的子序列;
  • 最长公共子序列:两个序列的最长的公共子序列。

LCS 问题是一个经典的计算机科学问题,常常被用于比较两个字符串的相似度。

算法思路

最长公共子序列问题可以使用动态规划求解。我们可以使用一个二维数组 $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]$ 的值为 $dp[i - 1][j]$ 和 $dp[i][j - 1]$ 中的最大值。

通过计算 $dp$ 数组中的每一个元素,最终 $dp[m][n]$ 即为 $text1$ 和 $text2$ 的最长公共子序列的长度。

可以通过进行迭代的方式逐步填充 $dp$ 数组。为了避免重复计算,我们可以使用一个字典 $memo$,其中 $memo[i][j]$ 表示计算过的 $dp[i][j]$ 的值,如果之前计算过,则直接从字典中取值,否则进行计算。

代码实现
Python 实现
class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        m, n = len(text1), len(text2)
        memo = [[-1] * (n + 1) for _ in range(m + 1)]
        return self.dfs(text1, text2, m, n, memo)
        
    def dfs(self, text1, text2, i, j, memo):
        if memo[i][j] != -1:
            return memo[i][j]
        if i == 0 or j == 0:
            return 0
        if text1[i - 1] == text2[j - 1]:
            memo[i][j] = self.dfs(text1, text2, i - 1, j - 1, memo) + 1
        else:
            memo[i][j] = max(self.dfs(text1, text2, i - 1, j, memo), self.dfs(text1, text2, i, j - 1, memo))
        return memo[i][j]
Java 实现
class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int m = text1.length(), n = text2.length();
        int[][] memo = new int[m + 1][n + 1];
        for (int[] row : memo) {
            Arrays.fill(row, -1);
        }
        return dfs(text1, text2, m, n, memo);
    }

    private int dfs(String text1, String text2, int i, int j, int[][] memo) {
        if (memo[i][j] != -1) {
            return memo[i][j];
        }
        if (i == 0 || j == 0) {
            return 0;
        }
        if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
            memo[i][j] = dfs(text1, text2, i - 1, j - 1, memo) + 1;
        } else {
            memo[i][j] = Math.max(dfs(text1, text2, i - 1, j, memo), dfs(text1, text2, i, j - 1, memo));
        }
        return memo[i][j];
    }
}
总结
  • 最长公共子序列问题可以使用动态规划求解;
  • 在计算 $dp[i][j]$ 的值时,需要注意边界条件和状态转移方程;
  • 为了避免重复计算,可以使用记忆化搜索或动态规划的方式进行求解。