基本概念
一个数列 ,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则称为已知序列的最长公共子序列(The longest common subsequence)。
最长公共子序列中的元素在原序列中不一定是连续的。许多与数学、算法、随机矩阵理论(random matrix theory)、表示论相关的研究都会涉及最长公共子序列。
求解思路
比如说有两个随机数列,1 9 2 8 2 7 和 3 9 2 8 6 6,则它们的最长公共子序列便是9 2 8;9 1 2 7 8 2 和3 9 2 8 6 6的最长公共子序列也是9 2 8。
注意和最长公共子串(Longest Common Substirng)的区别,子串是必须连续的,而子序列不然。
我们可以递归的解决这个问题:
1.当两个串末尾相同时,那么最后的公共子序列便是删去末尾的两个串再求子问题+末尾字符
2.当末尾不同时,考虑一个删一个不删的两种情况,也是相同的子问题,取两者较长者。
代码示例:(string)
string lcs(string a,string b)
{
if(a.length()==0||b.length()==0) return "";
else if(a[a.length()-1]==b[b.length()-1]) return lcs(a.substr(0,a.length()-1),b.substr(0,b.length()-1))+a[a.length()-1];
else{
string t1=lcs(a.substr(0,a.length()-1),b);
string t2=lcs(a,b.substr(0,b.length()-1));
if(t1.length()>t2.length()) return t1;
else return t2;
}
}
虽然递归可以方便的求出子序列长度及子序列,但是递归在时间和空间上消耗较大,可以采用从短往长的方法递推(思路相同),采用数组存储字符串。具体见下面例题
例题 Poj 1458
题意:
求解最长公共子序列的长度
代码示例:
#include<iostream>
#include<cstdio>
#include<string>
#include<string.h>
using namespace std;
const int maxn=1000;
char str1[maxn];
char str2[maxn];
int dp[maxn][maxn];
int solve()
{
int len1=strlen(str1);
int len2=strlen(str2);
int i,j;
for(int i=0;i<=len1;++i) dp[i][0]=0;//无公共子序列
for(int i=0;i<=len2;++i) dp[0][i]=0;
for(int i=0;i<len1;++i)
for(j=0;j<len2;++j){
if(str1[i]==str2[j]) dp[i+1][j+1]=dp[i][j]+1;
else{
dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
}
}
return dp[len1][len2];
}
int main()
{
ios::sync_with_stdio(false);
while(cin>>str1>>str2){
cout<<solve()<<endl;
}
return 0;
}
本题只要求解长度,如果需要具体序列,可以沿着[len1][len2]向左上寻找(逆着的过程),如果有多种走法,则说明存在不同位置选择的最长公共子序列。