news 2026/6/10 1:45:05

0x3f第14天 最长公共子序列

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
0x3f第14天 最长公共子序列

1.子数组/子串是连续的 子序列不一定是连续的 abcde中ace就是子序列

2.给定两个字符串,求两个的最长公共子序列,dfs(i,j)定义:序列1的前i个字母和序列2的前j个字母 的最长公共子序列

下一个问题:序列1前i-1个字母和序列二前j-1个字母

序列1前i个字母和序列二前j-1个字母

序列1前i-1个字母和序列二前j个字母

可以简化:

dfs(i,j) = dfs(i-1,j-1)+1 s[i]=t[j]

dfs(i,j) = max(dfs(i-1,j),dfs(i,j-1)) s[i]≠t[j]

最长公共子序列

1.回溯法

时间复杂度 nm 空间复杂度 nm

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

2.递推

先得到递推公式

f[i][j] = f[i-1][j-1]+1 s[i]=t[j]

max(f[i-1][j],f[i][j-1] s[i]≠t[j]

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]+1 else: f[i+1][j+1] = max(f[i][j+1],f[i+1][j]) return f[n][m]

3.空间优化

可以发现更新f[i+1][j+1]需要三个值,左边,上面,左上,但左上的数据会被覆盖,引入pre变量

pre = f[0]不能写在循环外面,核心原因是:进入 j 循环后,pre会逐步保存「上一个 j 位置的旧值」,精准匹配二维 DP 中dp[i-1][j-1]的语义。

空间复杂度 m

f = [0]*(m+1) for i,x in enumerate(text1): pre = f[0] for j,y in enumerate(text2): tmp = f[j+1] if x==y: f[j+1] = pre+1 else: f[j+1] = max(f[j+1],f[j]) pre = tmp return f[m]

编辑距离

为什么叫编辑距离:

“编辑”:指对字符串的修改操作(插入、删除、替换是文本编辑中最基础的三种操作)

距离: 操作次数越少,差异越小,距离越近

dfs(i, j)的定义表示将s[0..i](s 的前 i+1 个字符)转换为t[0..j](t 的前 j+1 个字符)所需的最少操作数

dfs(i-1, j) + 1→ 对应「删除操作」

含义:先将s[0..i-1]转换为t[0..j](操作数是dfs(i-1, j)),再删除s[i](+1 次操作)

dfs(i, j-1) + 1→ 对应「插入操作」

  • 含义:先将s[0..i]转换为t[0..j-1](操作数是dfs(i, j-1)),再插入t[j]到 s 中(+1 次操作)。

dfs(i-1, j-1) + 1→ 对应「替换操作」

含义:先将s[0..i-1]转换为t[0..j-1](操作数是dfs(i-1, j-1)),再s[i]替换为t[j](+1 次操作)

而如果s[i] == t[j],则不需要任何操作,直接继承dfs(i-1, j-1)的结果(这是 “无操作” 的情况)。

1.回溯法

1.回溯公式:

dfs(i,j) = dfs(i-1,j-1) if s[i] == t[j]

dfs(i,j) = min(dfs(i,j-1),dfs(i-1,j),dfs(i-1,j-1)) +1 if s[i] ≠ t[j]

插入 删除 替换

2.边界条件:if i<0:return j+1 把j全加一遍,就相同了

if j<0:return i+1 把i全删了,就相同了

class Solution: def minDistance(self, word1: str, word2: str) -> int: n = len(word1) m = len(word2) @cache def dfs(i,j): if i<0:return j+1 if j<0:return i+1 if word1[i]==word2[j]: return dfs(i-1,j-1) else: return min(dfs(i,j-1),dfs(i-1,j),dfs(i-1,j-1))+1 return dfs(n-1,m-1)

2.递推:

我卡住的地方:

if i<0:return j+1
if j<0:return i+1 怎么转换到二维数组里

首先 dfs 的定义是将 text1 前 i 转换为 text2 前 j 最少需要的操作次数

DP 的f[0][j]:对应递归的i=-1,此时 DP 的j对应递归的j-1→ 应返回(j-1)+1 = j(而非j+1

综上:i<0 对应的是 f[0][j] 数组的第一行 j+1 在这是 j

j<0对应的是 f[i][0]数组的第一列 i+1在这是i

所以边界条件:

for j in range(m+1):

f[0][j] = j

for i in range(n+1):

f[i][0] = i

f = [[0]*(m+1)for _ in range(n+1)] for j in range(m+1): f[0][j] = j for i in range(n+1): f[i][0] = i for i,x in enumerate(word1): for j,y in enumerate(word2): if x==y: f[i+1][j+1] = f[i][j] else: f[i+1][j+1] = min(f[i+1][j],f[i][j+1],f[i][j])+1 return f[n][m]

3.空间优化

和最长公子序列不同的是,这个题的二维数组的第一行和第一列是动态的,而最长公子序列的第一行和第一列都是0,所以这个题要考虑的更多

1.一维数组的创建:

f = [j for j in range(m+1)] 并不是简单的创了一个全是0 的数组

2.pre = f[0] f[0] = i+1 pre记录旧值,f[0] = i+1对应的是

dfs的 for i in range(n+1):
f[i][0] = i的操作

综合起来达到动态的f[0]的效果

f = [j for j in range(m+1)] for i,x in enumerate(word1): pre = f[0] f[0] = i+1 for j,y in enumerate(word2): temp = f[j+1] if x==y: f[j+1] = pre else: f[j+1] = min(f[j],f[j+1],pre) +1 pre=temp return f[m]

最长递增子序列

1.回溯法:

若用常见的选或不选思路进行回溯,会递归遍历所有可能的子序列,比如数组长度为 n 时,子序列总数是 2ⁿ

修改思路,dfs(i)专注于 “以 i 结尾” 的子序列长度

外部遍历所有i,是为了覆盖 “以任意元素结尾” 的所有可能,从而得到全局最长长度。

此时dfs(i)的定义:nums[i]结尾的最长递增子序列长度

外边再遍历一遍所有结尾情况,就能得出答案了

回溯公式:

dfs(i) = max {dfs(j)} +1 j<i and nums[j]<nums[i]

回溯代码:

def dfs(i):

temp = 0

for j in range(i):

if nums[j] < nums[i]:

temp = max(temp,dfs[j])

return temp+1

class Solution: def lengthOfLIS(self, nums: List[int]) -> int: n = len(nums) ans = 0 @cache def dfs(i): temp=0 for j in range(i): if nums[j]<nums[i]: temp = max(temp,dfs(j)) return temp+1 for i in range(n): ans = max(ans,dfs(i)) return ans

2.递推

递推公式:

f[i] = max{f(j)} +1 j<i and nums[j]<nums[i]

f = [0]*n for i in range(n): for j in range(i): if nums[j]<nums[i]: f[i] = max(f[i],f[j]) f[i] += 1 return max(f)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/5 1:52:48

1400万部,iPhone17奠定了它的王者之位,还有谁敢挑战它?

有分析人士指出截至2025年第50周&#xff0c;iPhone17在中国市场的销量已突破1400万部&#xff0c;奠定了它在国内旗舰手机的王者之位&#xff0c;到了此时恐怕已没有任何国产旗舰手机可以挑战它的市场地位了&#xff0c;四季度苹果夺下国内手机市场王座也没啥疑问了。iPhone17…

作者头像 李华
网站建设 2026/6/1 21:05:11

Unity游戏翻译神器:XUnity.AutoTranslator终极配置指南

Unity游戏翻译神器&#xff1a;XUnity.AutoTranslator终极配置指南 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 想要轻松实现Unity游戏的自动翻译吗&#xff1f;XUnity.AutoTranslator这款强大的Unity…

作者头像 李华
网站建设 2026/6/4 23:46:58

SillyTavern桌面应用:跨平台一键启动完整指南

SillyTavern桌面应用&#xff1a;跨平台一键启动完整指南 【免费下载链接】SillyTavern LLM Frontend for Power Users. 项目地址: https://gitcode.com/GitHub_Trending/si/SillyTavern 还在为每次启动AI聊天工具都要打开命令行而烦恼吗&#xff1f;你是否渴望像使用普…

作者头像 李华
网站建设 2026/5/31 7:37:46

哔哩下载姬深度解析:从入门到精通的B站视频收藏全攻略

哔哩下载姬深度解析&#xff1a;从入门到精通的B站视频收藏全攻略 【免费下载链接】downkyi 哔哩下载姬downkyi&#xff0c;哔哩哔哩网站视频下载工具&#xff0c;支持批量下载&#xff0c;支持8K、HDR、杜比视界&#xff0c;提供工具箱&#xff08;音视频提取、去水印等&#…

作者头像 李华
网站建设 2026/5/26 22:06:25

纪念币预约神器:告别手速不够的烦恼,轻松搞定限量收藏

还记得那些守在电脑前&#xff0c;手指颤抖着等待纪念币预约开始的时刻吗&#xff1f;眼看着心仪的纪念币在几秒内被抢光&#xff0c;那种失落感简直让人崩溃。但现在&#xff0c;这一切都将成为过去&#xff01;让我分享一个真实的使用故事&#xff0c;看看这个智能工具如何改…

作者头像 李华