最长公共子序列问题(longest-common-subsequence problem) 问题解释:对两个序列X,Y,求他们的长度最长的公共子序列。子序列:例如,X = {5, 6, 7},那么{5} {6} {7} {5,6} {5,7} {6,7} {5,6,7}, 空集都是X的子序列。 暴力求解:用暴力求解,那么就要穷举X的所有子序列,然后一个个检查X的子序列是否也是Y的子序列,找到最长的公共子序列,m长度的X序列有2^m个,所以暴力方法的时间复杂度为指数阶,并不实用。
1.符号:X[i],Y[j]:X,Y序列; c[i][j]:表示X序列前i个和Y序列前j个的LCS长度, b[i][j]:记录i, j的走向 2.递归方程:
两个序列的LCS包含两个序列前缀的LCS,因此,LCS问题具有最优子结构性质。可用动态规划求解。 解释:举个例子,X = {A,B,C,B},Y = {A,B,A,B},LCS = {A, B, B}, 若设定LCS最后一个B为去掉,它对应的是XY最后一个B,此时X1 = {A, B, C} 是X的前缀, Y2 = {A, B, A}是Y的前缀, 这时,X2和Y2的LCS为{A, B}是X和Y的前缀。
递推公式 c[i][j] = 0; i = 0 或 j = 0 //当有序列长度为0时是没有公共子序列的 = c[i – 1][j – 1] + 1; X[i] = Y[j] //X序列第i个和Y序列第j个相等, 则加一 = max{c[i][j – 1], c[i – 1][j]} //在X[i]、Y[j – 1]和X[i – 1]、Y[j]中找最长的LCS赋给c[i][j] 用次递归方法人工查找: X = A B C B D A B Y = B D C A B A 1.X[1]是否等于Y[1] ,否
2.找X[2]是否等于Y[1] 或者X[1]是否等于Y[2],
X[2]等于Y[1],LCS第一个为X[2]
3.X[2 + 1]是否等于Y[1 + 1], 否
。。。重复找LCS 代码:
#include<iostream>
using namespace std;
//定义c[i,j]表示x链前i个和y链前j个的LCS的长度
char X[100]; //x链
char Y[100]; //y链
int c[100][100]; //LCS个数
int b[100][100]; //记录走势,1表示i, j都减一,2表示i-1,3表示j-1
void LCS(char X[], char Y[], int n, int m) { //n,m为X,Y链的长度
//第一步先设定临界值
for(int i = 0; i <= m; i++)
c[0][i] = 0;
for(int i = 1; i <= n; i++)
c[i][0] = 0;
//主体部分
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
if(X[i] == Y[j]) {
c[i][j] = c[i - 1][j - 1] + 1;
b[i][j] = 1;
}
else if(c[i - 1][j] >= c[i][j - 1]){
c[i][j] = c[i - 1][j];
b[i][j] = 2;
}
else {
c[i][j] = c[i][j - 1];
b[i][j] = 3;
}
}
void print(char X[], char Y[], int i, int j) {
if(i == 0 || j == 0)
return;
if(b[i][j] == 1) {
print(X, Y, i - 1, j - 1);//从后往前搜索所有的字母后再打印字母
cout<<X[i];
}
else if(b[i][j] == 2)
print(X, Y, i - 1, j);
else
print(X, Y, i, j - 1);
}
int main() {
int n, m;
X[0] = '0'; Y[0] = '0'; //链的第0个不用,可写可不写
cin>>n>>m;
for(int i = 1; i <= n; i++)
cin>>X[i];
for(int i = 1; i <= m; i++)
cin>>Y[i];
LCS(X,Y,n,m);
cout<<c[n][m]<<endl;
print(X, Y, n, m);
}
/*
7 6
A B C B D A B
B D C A B A
*/
自学算法导论上的LCS,欢迎指错,菜鸟一枚,大神勿喷