当前位置: 首页 > news >正文

多维动态规划题解——最长公共子序列【LeetCode】记忆化搜索翻译成递推

143. 最长公共子序列

方法一:记忆化搜索


一、算法逻辑(每一步思路)

❓ 问题描述:

给定两个字符串 text1text2,返回它们的最长公共子序列的长度

公共子序列:在两个字符串中都出现过,且相对顺序一致,但不要求连续。


✅ 解题思路(递归 + 记忆化)

1. 状态定义:

定义 dfs(i, j) 表示:

  • text1[0..i]text2[0..j] 中的最长公共子序列的长度。
2. 状态转移:
if text1[i] == text2[j]:dfs(i, j) = dfs(i-1, j-1) + 1
else:dfs(i, j) = max(dfs(i-1, j), dfs(i, j-1))

解释:

  • 如果当前两个字符相等,则这个字符可以作为公共子序列的一部分,答案 +1;
  • 如果不相等,我们可以选择跳过一个字符,取两种可能中的最大值。
3. 终止条件(Base Case):
if i < 0 or j < 0:return 0

表示有一个字符串已经空了,公共子序列长度为 0。

4. 返回值:

最终返回 dfs(n - 1, m - 1),即原字符串全部范围的 LCS 长度。


二、算法核心点

✅ 核心技巧:记忆化搜索 + 状态递归

  • 通过 @cache 进行记忆化,避免重复计算(如果不用 cache,会超时);
  • 状态空间是 (i, j) 二维的。

这等价于二维动态规划的形式,但使用递归写法逻辑更清晰。

class Solution:def longestCommonSubsequence(self, text1: str, text2: str) -> int:n, m = len(text1), len(text2)@cachedef dfs(i:int, j:int) -> int:if i<0 or j<0:return 0if text1[i] == text2[j]:return dfs(i-1, j-1)+1return max(dfs(i-1, j), dfs(i, j-1))return dfs(n-1, m-1)

三、复杂度分析

  • 时间复杂度:O(n * m)
    每个 (i, j) 状态只会被访问一次。
  • 空间复杂度:O(n * m)
    用于保存缓存结果(memo)和递归栈深度。

✅ 举个例子

输入:

text1 = "abcde"
text2 = "ace"

执行过程:

"abcde" 与 "ace"
比较 'e' 和 'e' -> 相等 => +1,继续比较 'd' 与 'c' ...
最终返回公共子序列是 "ace",长度为 3。

✅ 总结表

维度

内容

✅ 思路逻辑

定义 dfs(i, j):text1[0..i] 与 text2[0..j] 的 LCS 长度

✅ 状态转移

相等则 +1,否者取左/上两种子问题最大值

✅ 技巧

递归 + 记忆化(Top-down DP)

✅ 时间复杂度

O(n * m)

✅ 空间复杂度

O(n * m)

 

方法一:1:1 翻译成递推


✅ 一、核心思路解析

❓ 问题:

给定两个字符串 text1text2,找出它们的最长公共子序列的长度。


✅ 解题思路(二维 DP)

1. 状态定义:

定义二维数组 f

  • f[i][j] 表示 text1[0..i-1]text2[0..j-1] 的最长公共子序列长度。

注意这里的偏移:我们实际比较的是字符串的前 i 和前 j 个字符,但 f 的下标是从 1 开始的(第 0 行/列为 base case)。


2. 状态转移方程:
if text1[i] == text2[j]:f[i+1][j+1] = f[i][j] + 1
else:f[i+1][j+1] = max(f[i][j+1], f[i+1][j])

解释:

  • 如果两个字符相等,则这个字符可以加入公共子序列。
  • 否则,继承“去掉当前一个字符”的最优解。

3. 初始化:
f = [[0] * (m+1) for _ in range(n+1)]

第一行和第一列代表一个字符串为空时,LCS 长度为 0。


4. 最终答案:
return f[n][m]

表示完整的两个字符串的 LCS 长度。

class Solution:def longestCommonSubsequence(self, text1: str, text2: str) -> int:n, m = len(text1), len(text2)f = [[0]*(m+1) for _ in range(n+1)]for i,x in enumerate(text1):for j,y in enumerate(text2):if x==y:f[i+1][j+1] = f[i][j]+1else:f[i+1][j+1] = max(f[i][j+1], f[i+1][j])return f[n][m]

✅ 二、图解对比示意

假设输入:

text1 = "abcde"
text2 = "ace"

DP 表格 f:

""

a

c

e

""

0

0

0

0

a

0

1

1

1

b

0

1

1

1

c

0

1

2

2

d

0

1

2

2

e

0

1

2

3

结果是 3,对应 LCS 为 "ace"


✅ 三、复杂度分析

  • 时间复杂度:O(n * m)
    两层循环枚举所有子串组合。
  • 空间复杂度:O(n * m)
    使用了二维数组 f[n+1][m+1]

可进一步优化为 O(m) 空间,用一维滚动数组,但二维形式更直观清晰。


✅ 总结表格

项目

内容

状态定义

f[i][j]: text1 前 i 字符与 text2 前 j 字符的 LCS 长度

状态转移

如果相等:f[i+1][j+1] = f[i][j] + 1;否则:取最大

初始值

f[0][*] = f[*][0] = 0

返回值

f[n][m]

时间复杂度

O(n * m)

空间复杂度

O(n * m)(可优化)

http://www.dtcms.com/a/284332.html

相关文章:

  • CCS缺陷|冻干/灌装10大缺陷暴露无菌生产系统性漏洞:气流流型缺陷
  • 【android bluetooth 协议分析 03】【蓝牙扫描详解 3】【Bluetooth 中 EIR、IR、BLE 普通广播与扩展广播详解】
  • 数仓建设中,系统数据录入错误或者延迟,如何对历史数据修复或补入?
  • 安装物理机ubuntu系统
  • 初试Spring AI实现聊天功能
  • PyCharm 高效入门指南(引言 + 核心模块详解)
  • ubuntu22 npm install electron --save-dev 失败
  • 基于Rust游戏引擎实践(Game)
  • 智能体开发工具链全景图:IDE、调试器与监控平台
  • 营业执照识别-营业执照文字识别API-营业执照真伪
  • Flutter状态管理篇之ChangeNotifier(一)
  • 什么是协变(Covariant)与逆变(Contravariant)?
  • 在 Windows Server RDS 上配置用户配置文件磁盘查找对应的用户名
  • 第2章通用的高并发架构设计——2.5 高并发读场景总结:CQRS
  • 【计算机网络】第四章:网络层(上)
  • Spring中事务失效的情况深度分析
  • 深入理解 KVM 子系统:从虚拟化核心到内核实现的全景解析
  • 7.18 note
  • 第十八节:第三部分:java高级:反射-获取构造器对象并使用
  • 【参数初始化】——深度学习.全连接网络
  • Cadence SPB 2024软件下载及安装教程|Cadence软件安装详细步骤附下载链接
  • Spring IoCDI_2_使用篇
  • Anime.js 超级炫酷的网页动画库详解
  • 【图像质量评价指标】信噪比(Signal-to-Noise Ratio,SNR)
  • Linux 基础指令
  • makefile missing separator. Stop.问题
  • 一款实用的.NET Core加密解密工具类库
  • C++11之lambda表达式与包装器
  • 20.轮廓特征与近似,改变图像的轮廓识别画线的精确度,同时画出轮廓对应的矩形
  • GitHub 趋势日报 (2025年07月16日)