问题的定义:
- 子序列
- X=(A, B, C, B, D, B)
- Z=(B, C, D, B)是X的子序例
- W=(B, D, A)不是X的子序例
- 公共子序列
- Z是序列X与Y的公共子序列如果Z是X的子序也是Y的子序列。
最长公共子序列(LCS)问题
输入:X = (x1, x2, …, xn),Y = (y1, y2, …, ym)
输出:Z = X与Y的最长公共子序列
蛮力法:
- 枚举X的每个子序列Z;
- 检查Z是否为Y的子序列;
- T(n)=O(m×2n)。
动态规划:
优化子结构:
设X = (x1, x2, …, xn),Y = (y1, y2, …, ym) 是两个序列,Z=(z1, z2, …, zn)是X与Y的LCS,我们有:- 如果xm = yn, 则zk = xm = yn, Zk-1是Xm-1和Yn-1的LCS,即,LCSXY = LCSXm-1Yn-1+ (xm = yn)。
- 如果xm ≠ yn,且zk ≠ xm,则Z是Xm-1和Y的LCS,即LCSXY= LCSXm-1Y
- 如果xm ≠ yn,且zk ≠ yn,则Z是X和Yn-1的LCS,即LCSXY= LCSXYn-1
递归方程:
- C[i, j] = Xi与Yj 的LCS的长度
- LCS长度的递归方程
- C[i, j] = 0 , if i=0 or j=0
- C[i, j] = C[i-1, j-1] + 1, if i, j>0 and xii = yjj
- C[i, j] = Max(C[i, j-1], C[i-1, j]), if i, j>0 and xii yjj
基本思想:
在知道C[i-1, j-1], C[i, j-1], C[i-1, j]的情况下,根据递归方程计算C[i, j]。
数据结构:
- C[0:m,0:n]: C[i,j]是Xi与Yj的LCS的长度
- B[1:m,1:n]: B[i,j]是指针,指向计算C[i,j]时所选择的子问题的优化解所对应的C表的表项
伪代码:
LCS-length(X, Y)
m = length(X);
n = length(Y);
For i = 1 To m Do
C[i,0] = 0;
For j = 1 To n Do
C[0,j] = 0;
For i = 1 To m Do
For j = 1 To n Do
If xi = yj Then
C[i,j] = C[i-1,j-1] + 1;
B[i,j] = “↖”;
Else If C[i-1,j]>=C[i,j-1] Then
C[i,j] = C[i-1,j];
B[i,j] = “↑”;
Else C[i,j] = C[i,j-1];
B[i,j] = “←”;
Return C and B.
C++代码:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
vector<vector<int>> b, c;
void lcs(string str1, string str2, int len1, int len2)
{
b.resize(len1 + 1);
c.resize(len1 + 1);
for (int i = 0; i < len1 + 1; i++)
{
b[i].resize(len2 + 1, 0);
c[i].resize(len2 + 1, 0);
}
for (int i = 1; i <= len1; i++)
{
for (int j = 1; j <= len2; j++)
{
if (str1[i - 1] == str2[j - 1])
{
c[i][j] = c[i - 1][j - 1] + 1;
b[i][j] = 0;
}
else if(c[i - 1][j] >= c[i][j - 1])
{
c[i][j] = c[i - 1][j];
b[i][j] = 1;
}
else
{
c[i][j] = c[i][j - 1];
b[i][j] = -1;
}
}
}
}
void PrintLCS(vector<vector<int>> b, string str1, int i, int j)
{
if (i == 0 || j == 0)
return;
if (b[i][j] == 0)
{
PrintLCS(b, str1, i - 1, j - 1);
cout << str1[i - 1] << " ";
}
else if (b[i][j] == 1)
{
PrintLCS(b, str1, i - 1, j);
}
else
{
PrintLCS(b, str1, i, j - 1);
}
}
int main()
{
string str1 = "abcdef";
string str2 = "acef";
int len1 = str1.size();
int len2 = str2.size();
b.resize(len1 + 1);
c.resize(len2 + 1);
lcs(str1, str2, len1, len2);
cout << c[len1][len2] << endl;
PrintLCS(b, str1, len1, len2);
return 0;
}