最长公共子序列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];
posted @   才瓯  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示