最长公共子序列
最长公共子序列问题的定义如下:设有两个序列S1[1..m]和S2[1..n],需要寻找它们之间的一个最长公共子序列。
例如,我们有如下两个序列:
S1:INTHEBEGINNING
S2:ALLTHINGSARELOST
则S1和S2的一个最长公共子序列为THING。
这里需注意的是,一个子序列不一定必须是连续的,即中间可以被其他字符分开,但它们的顺序必须正确的。另外,最长公共子序列不一定只有一个,而我们要寻找的是其中一个。
例如,我们有如下两个序列:
S1:INTHEBEGINNING
S2:ALLTHINGSARELOST
则S1和S2的一个最长公共子序列为THING。
这里需注意的是,一个子序列不一定必须是连续的,即中间可以被其他字符分开,但它们的顺序必须正确的。另外,最长公共子序列不一定只有一个,而我们要寻找的是其中一个。
一、第一种解法:蛮力策略
⑴ 检查S1[1..m]里面每一个序列。
⑵ 看看其是否也是S2[1..n]里的子序列。
⑶ 在每一步记录当前找到的子序列里面最长的子序列。
分析其运行效率,对每一个子序列的检查需要时间O(n),而总共有2^m子序列需要检查,因此本算法的最坏时间复杂性是O(n×2^m)。
⑵ 看看其是否也是S2[1..n]里的子序列。
⑶ 在每一步记录当前找到的子序列里面最长的子序列。
分析其运行效率,对每一个子序列的检查需要时间O(n),而总共有2^m子序列需要检查,因此本算法的最坏时间复杂性是O(n×2^m)。
二、动态规划
利用动态规划寻找最长公共子序列的步骤如下:
⑴ 先寻找最长公共子序列的长度。
⑵ 扩展寻找长度的算法来获得最长公共子序列。
策略:考虑序列S和S的前缀序列。
设c[i,j] = |LCS(S1[1..i], S2[1..j])|,则有c[m,n] = |LCS(S1, S2)|
由此可以总结出通式:
利用动态规划寻找最长公共子序列的步骤如下:
⑴ 先寻找最长公共子序列的长度。
⑵ 扩展寻找长度的算法来获得最长公共子序列。
策略:考虑序列S和S的前缀序列。
设c[i,j] = |LCS(S1[1..i], S2[1..j])|,则有c[m,n] = |LCS(S1, S2)|
由此可以总结出通式:
如果设LCS(S1, S2,i,j)表示S1[1..i]和S2[1..j]的最长公共子序列的长度,则有:
计算最长公共子序列长度的动态规划算法如下:
代码:
#include<iostream>
#include<cstring>
// poj1458/hduoj1159
using namespace std;
const int maxlen = 20;
void LCS(int m[][maxlen], int b[][maxlen], char x[], char y[], int xlen, int ylen)
{
int i, j;
for(i=0; i<=ylen; ++i)
m[0][i] = 0;
for(i=0; i<=ylen; ++i)
m[i][0] = 0;
memset(b,0,sizeof(b));
for(i=1; i<=ylen; ++i){
// 1 - leftup 2 - up 3 - down
for(j=1; j<=xlen; ++j){
if(x[j-1]!=y[i-1]){
if(m[i-1][j]>m[i][j-1]){
m[i][j] = m[i-1][j];
b[i][j] = 2;
}else{
m[i][j] = m[i][j-1];
b[i][j] = 3;
}
}else{
m[i][j] = m[i-1][j-1] + 1;
b[i][j] = 1;
}
}
}
}
void printLCS(int b[][maxlen], char x[], int row, int col)
{
if(row==0 || col==0)
return;
if(b[row][col]==1){
printLCS(b,x,row-1,col-1);
cout << x[col-1];
}
else if(b[row][col]==2){
printLCS(b,x,row-1,col);
}else{
printLCS(b,x,row,col-1);
}
return;
}
int main()
{
char x[maxlen] = "INTHEBEGINNING";
char y[maxlen] = "ALLTHINGSARELOST";
int xlen = strlen(x), ylen = strlen(y);
int m[maxlen][maxlen], b[maxlen][maxlen];
int i, j;
memset(b,0,sizeof(b));
LCS(m,b,x,y,xlen,ylen);
printLCS(b,x,ylen,xlen);
cout << endl;
/*for(i=0; i<=ylen; ++i){
for(j=0; j<=xlen; ++j){
cout << b[i][j] << " ";
}
cout << endl;
}*/
return 0;
}