1. 问题
查找两个序列的最长公共子序列
最长公共子序列的结构有如下表示:
设序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的一个最长公共子序列Z=<z1, z2, …, zk>,
则:
若xm=yn,则zk=xm=yn且Zk-1是Xm-1和Yn-1的最长公共子序列;
若xm≠yn且zk≠xm ,则Z是Xm-1和Y的最长公共子序列;
若xm≠yn且zk≠yn ,则Z是X和Yn-1的最长公共子序列。
其中Xm-1=<x1, x2, …, xm-1>,Yn-1=<y1, y2, …, yn-1>,Zk-1=<z1, z2, …, zk-1>。
2. 解析
我采用的两个序列为
S1 = <GCCCTAGCGDE>
S2 = <GCGCAATGDE>
为了用动态编程有效地计算 LCS,首先需要构建一个表格,用它保存部分结果。沿着顶部列出一个序列,再沿着左侧从上到下列出另一个序列。
1.先建立一张表格
2.初始化表示,将第一行和第一列全部设为0
3.填充表格(箭头表示回溯,即这次结果可以作为下次的已知条件利用)
4.通过这种回溯方法,得到的 LCS 为 GCCAG
3. 设计
string LCS(string s1, string s2)
{
if (s1 == "" || s2 == "")
return "";
int m = s1.size() + 1;
int n = s2.size() + 1;
int lcs[m][n], i, j;
for (i = 0; i < m; i++)
for (j = 0; j < n; j++)
lcs[i][j] = 0;
for (i = 1; i < m; i++)
for (j = 1; j < n; j++)
{
if (s1[i - 1] == s2[j - 1])
lcs[i][j] = lcs[i - 1][j - 1] + 1;
else
lcs[i][j] = lcs[i - 1][j] >= lcs[i][j - 1] ? lcs[i - 1][j] : lcs[i][j - 1];//取上面或左侧最大值
}
i = m - 2;
j = n - 2;
string ss = ""
while (i != -1 && j != -1)
{
if (x[i] == y[j])
{
ss += s[i];
i--;
j--;
}
else
{
if (lcs[i + 1][j + 1] == lcs[i][j])
{
i--;
j--;
}
else
{
if (lcs[i][j + 1] >= lcs[i + 1][j])
i--;
else
j--;
}
}
}
reverse(ss);//逆转ss
return ss;
}
4. 分析
时间复杂度O(n)=mn