📜  最长公共子序列| DP使用备忘(1)

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

最长公共子序列 | DP使用备忘

最长公共子序列(LCS)问题是计算两个字符串的最长公共子序列的问题。例如,字符串“ABACBDAB”和“BDCABA”有公共子序列“BCBA”,长度为4。

动态规划解法使用备忘录,避免无效计算,提高执行效率。

DP状态转移方程

设 $f(i, j)$ 表示 $s1$ 从 $1$ 到 $i$ 与 $s2$ 从 $1$ 到 $j$ 的最长公共子序列长度,若 $s1_i = s2_j$,则 $f(i, j) = f(i-1, j-1) +1$,否则 $f(i, j) = max(f(i-1, j), f(i, j-1))$

即,当末尾字符相同时,公共子序列长度加1,其他情况则考虑舍去一个字符,继续寻找公共子序列。

DP使用备忘录

无论是暴力递归还是DP,都存在大量的重复计算,可以使用备忘录优化。

备忘录原理是对每个状态保存已经计算的答案,下次遇到相同状态时直接返回答案而不是重复计算。

DP算法的动态转移过程可以用二维数组保存,所以可以使用一个 $n * m$ 二维数组作为备忘录记录已经求解的状态。

代码实现

使用Python实现最长公共子序列算法,DP使用备忘录优化。

def LCS(s1: str, s2: str) -> int:
    n, m = len(s1), len(s2)
    memo = [[0 for j in range(m)] for i in range(n)]
    def dp(i: int, j: int) -> int:
        if i == -1 or j == -1:
            return 0
        if memo[i][j] != 0:
            return memo[i][j]
        if s1[i] == s2[j]:
            memo[i][j] = dp(i-1, j-1) + 1
        else:
            memo[i][j] = max(dp(i-1, j), dp(i, j-1))
        return memo[i][j]
    return dp(n-1, m-1)

上述代码中,memo 是备忘录数组,dp(i, j) 是求解 $f(i, j)$ 的递归函数,每次递归前先查看 $f(i, j)$ 是否已经计算,若是则直接返回结果。最终返回 $f(n-1, m-1)$ 即为最长公共子序列长度。

总结

最长公共子序列问题是字符串处理中的经典问题,DP算法解法使用备忘录可以提高性能,避免无效计算。备忘录机制是动态规划算法优化的重要一环,可以应用于各种类型的DP问题中,提升程序执行效率。