[1143][中等][动态规划] 最长公共子序列

题目描述

1143. 最长公共子序列

583. 两个字符串的删除操作

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

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。

若这两个字符串没有公共子序列,则返回 0。

示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace",它的长度为 3。

示例 2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc",它的长度为 3。

示例 3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0。

提示:

  • 1 <= text1.length <= 1000

  • 1 <= text2.length <= 1000

  • 输入的字符串只含有小写英文字符。

解题思路

动态规划之最长公共子序列(LCS)

空间优化点

使用滚动数组的思想对使用的空间进行优化, 原本二维的状态转移表的空间复杂度为O(mn)O(mn), 其中mmnn分别是两个字符串的长度. 观察状态转移方程, 对于(j,i)(j, i), 使用到的所有依赖为(j1,i1)(j-1,i-1), (j,i1)(j,i-1), (j1,i)(j-1,i). 所以我们仅存储:

  • 上一列i1i-1的结果

这样, 从前到后(状态表从上到下)循环时, 在计算(j,i)(j, i), 已经计算得到了(j1,i)(j-1,i), (j1,i)(j-1,i)覆盖了(j1,i1)(j-1,i-1). 这样我们就能计算得到(j,i)(j, i), 用来覆盖(j,i1)(j,i-1), 从而在列的维度上逐步推进.

需要注意(j1,i)(j-1,i)覆盖了(j1,i1)(j-1,i-1)的值, 而在计算(j,i)(j, i)又需要(j1,i1)(j-1,i-1)的值, 因此需要一个额外的标量变量来存储左上角(即(j1,i1)(j-1,i-1))的值.

代码如下:

class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        n, m = len(text1), len(text2)
        cache = [0] * n
        for i in range(m):
            for j in range(n):
                if j == 0:
                    cache[j] = 1 if text1[j] == text2[i] else cache[j]
                    la = cache[j]  # 为下一轮备用, 记录左上角的值
                else:
                    tla = cache[j]
                    cache[j] = la + 1 if text1[j] == text2[i] else max(cache[j], cache[j - 1])
                    la = tla  # 为下一轮备用, 记录左上角的值
        return cache[-1]

相关题目

最后更新于

这有帮助吗?