最长公共子序列LCS
( longest common subsequence )
问题描述
- 一个给定序列的子序列是该给定序列中去掉0个或者多个元素,此处的子序列不要求必须连续.
- 给定序列 X 和
Y , 如果 Z 既是X 的一个子序列,又是 Y 的一个子序列, 则Z 是 X 和Y 的公共子序列. - 找出给定序列 X 和
Y 的最长公共子序列 LCS.
问题分析
- 穷举方法:强力枚举出X 的所有子序列,然后逐一检查是否为
Y 的子序列. 假设 X 有m 个元素,其子序列有 2m 个,需要指数时间. - 动态规划法:LCS 问题具有最优子结构,可以将指数时间化为多项式时间.
LCS 动态规划最优子结构
- 假设Zk=<z1,z2,...,zk>为Xm和Yn的任意LCS,序列Xm=<x1,x2,...,xm>,Yn=<y1,y2,...,yn>.
- 如果xm=yn, 那么zk=xm=yn,且Zk−1是Xm−1和Yn−1的一个LCS.
- 如果xm≠yn, 则zk≠xm, Zk是Xm−1和Yn的一个LCS.
- 如果xm≠yn, 则zk≠yn, Zk是Xm和Yn−1的一个LCS.
LCS 递归关系
定义c[i,j] 为Xi和Yj的一个LCS的长度.
c[i,j]=⎧⎩⎨0c[i−1,j−1]+1max{c[i,j−1],c[i−1,j]}i=0 or j=0i,j>0 and xi=jii,j>0 and xi≠ji
C++代码
#include <iostream>
#include <vector>
#include <string>
using namespace std;
typedef vector< vector<int> > mat2;
class Seqmatching
{
string seq1;
string seq2;
public:
Seqmatching();
Seqmatching(string seqc1, string seqc2);
~Seqmatching();
void matching(mat2 & seq_n, mat2 & tra) const;
void lcs(const mat2 & tra, string & subseq, int i, int j) const;
};
Seqmatching::Seqmatching()
{
seq1 = "";
seq2 = "";
}
Seqmatching::Seqmatching(string seqc1, string seqc2)
{
seq1 = seqc1;
seq2 = seqc2;
}
Seqmatching::~Seqmatching()
{
}
void Seqmatching::matching(mat2 & seq_n, mat2 & tra) const
{
int n1 = seq1.length();
int n2 = seq2.length();
for (int i=1; i<=n1; ++i)
{
for (int j=1; j<=n2; ++j)
{
if (seq1.at(i-1) == seq2.at(j-1))
{
seq_n.at(i).at(j) = seq_n.at(i-1).at(j-1) + 1;
tra.at(i).at(j) = 3;
}
else
{
if (seq_n.at(i-1).at(j) >= seq_n.at(i).at(j-1))
{
seq_n.at(i).at(j) = seq_n.at(i-1).at(j);
tra.at(i).at(j) = 2;
}
else
{
seq_n.at(i).at(j) = seq_n.at(i).at(j-1);
tra.at(i).at(j) = 1;
}
}
}
}
}
void Seqmatching::lcs(const mat2 & tra, string & subseq, int i, int j) const
{
subseq.clear();
if (i!=0 && j!=0)
{
if (tra.at(i).at(j) == 3)
{
lcs(tra, subseq, i-1, j-1);
subseq += seq1.substr(i-1, 1);
}
else
{
if(tra.at(i).at(j) == 2)
{
lcs(tra, subseq, i-1, j);
}
else
{
lcs(tra, subseq, i, j-1);
}
}
}
}
int main(int argc, char** argv)
{ string seq1(argv[1]);
string seq2(argv[2]);
Seqmatching seqm(seq1,seq2);
int n1 = seq1.length();
int n2 = seq2.length();
mat2 seq_n(n1+1, vector<int>(n2+1));
mat2 tra(n1+1, vector<int>(n2+1));
seqm.matching(seq_n, tra);
string subseq;
seqm.lcs(tra, subseq, n1, n2);
cout<<seq1<<endl;
cout<<seq2<<endl;
cout<<seq_n.at(n1).at(n2)<<endl;
cout<<subseq<<endl;
return 0;
}
分析总结
- seq_n[i,j]存储最优子问题的解,tra[i,j]追溯最优解的位置,LCS 最终保存在subseq字符串中.
- 该算法的时间复杂度为O(mn). 其中计算最优子问题的时间为O(mn),后续追踪子序列为O(m+n).
本文详细解释了最长公共子序列(LCS)问题的解决方法,包括穷举法与动态规划法,以及如何使用C++实现动态规划算法。重点介绍了LCS的最优子结构、递归关系及时间复杂度。
3489

被折叠的 条评论
为什么被折叠?



