最长公共子序列LCS 笔记
最长公共子序列LCS 笔记
假设存在两个相同长度平凡的序列,我们希望找到它们最长的公共子序列,在没有其他特殊条件的情况下,利用动态规划计算的时间复杂度为 \(O(n^2)\) ,并且可以记录这个子序列
考虑两个指针作用于两个序列上,记 \(dp_{i,j}\) 表示为连续子序列 \([a_1,a_i]\) 与 \([b_1,b_j]\) 上的最长公共子序列的长度
两个指针当前指向的元素存在两种可能性:
-
\(a_i=b_j\) ,说明 \(a_i,b_j\) 都能存在于要找的公共子序列中,状态转移为
\[dp_{i,j}=dp_{i-1,j-1}+1 \] -
\(a_i\ne b_j\) ,有两种可能
- \(a_i\) 不在公共子序列中,\(dp_{i,j}=dp_{i-1,j}\)
- \(b_j\) 不在公共子序列中,\(dp_{i,j}=dp_{i,j-1}\)
两种可能合并后,当前的 \(dp_{i,j}\) 取最优解,状态转移为
\[dp_{i,j}=max(dp_{i-1,j},dp_{i,j-1}) \]
最后考虑边界情况,即当 \(i=0,j=0\) 时,\(dp_{i,j}=0\) ,这意味着一个序列没有元素时无法找出公共子序列
dp[0][0]=0; for (int i=1;i<=m;i++){ for (int j=1;j<=n;j++){ if (a[i]==b[j]) f[i][j]=f[i-1][j-1]+1; else f[i][j]=max(f[i][j-1],f[i-1][j]); } }
如果需要记录所选的最长公共子序列,则要添加一个前驱数组 \(p\),表明状态的转移方向
for (int i=1;i<=m;i++){ for (int j=1;j<=n;j++){ if (a[i-1]==b[j-1]){ dp[i][j]=dp[i-1][j-1]+1; p[i][j]=1; }else if (f[i][j-1]>f[i-1][j]){ f[i][j]=f[i][j-1]; p[i][j]=2; }else{ f[i][j]=f[i-1][j]; p[i][j]=3; } } }
将两个序列拆分后,分别为表格的长宽
\(p=1,2,3\) 分别表示状态转移的方向,左上方,左边,上边
最后通过依次倒序访问 \(p\) 数组,根据 \(p_{i,j}\) 移动游标,得到公共子序列
int i=m,j=n,k=dp[m][n]; vector<char> s(k+10); while (i>0&&j>0){ if (p[i][j]==1){ s[k--]=a[i-1];//记录公共子序列 i--; j--; }else if (p[i][j]==2) j--;//移动游标 else i--; } for (int i=1;i<=k;i++) cout<<s[i];
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具