最长公共序列
关于最长公共子序列,相信大家都不是很陌生了,在这里我们分两种情况讨论,一是子序列是不连续的,二是子序列是连续的。
题目:字符串A=“abcbdab”,字符串B=“bdcabc”,求A与B的最长公共子序列
(1)子序列是不连续的
关于子序列是不连续的,最长用的方法是动态规划,通式为:
x[i][j]=x[i-1][j-1] if A[i]==B[j]
x[i][j]=max{x[i-1][j],x[i][j-1]} if A[i]!=B[j]
代码实现:
View Code
#include <iostream>
#include <stack>
using namespace std;
const int N=100;
int c[N][N]; //c[i][j]记录字符串A[1...i]和B[1...j]中的最长公共子串
int LCS(char *A,char *B,int lenA,int lenB);
void main()
{
char A[8]={'','a','b','c','b','d','a','b'};
char B[7]={'','b','d','c','a','b','c'};
int lenA=7,lenB=6;
int len=LCS(A,B,lenA,lenB);
cout<<len<<endl;
int i=lenA,j=lenB;
stack<char> S;
while(i&&j) //输出最长公共子串
{
if(c[i][j]==c[i-1][j-1]+1) // 判断c[i][j]是如何得到的,并采取不同的措施
{
S.push(A[i]);
i--;
j--;
}
else if(c[i][j]==c[i-1][j])
i--;
else
j--;
}
while(!S.empty())
{
cout<<S.top()<<"";
S.pop();
}
cout<<endl;
}
int LCS(char *A,char *B,int lenA,int lenB)
{
int i,j;
memset(c,0,100*100);
for(i=0;i<=lenA;i++)
c[i][0]=0;
for(j=0;j<=lenB;j++)
c[0][j]=0;
for(i=1;i<=lenA;i++)
for(j=1;j<=lenB;j++)
{
if(A[i]==B[j])
c[i][j]=c[i-1][j-1]+1;
else
c[i][j]=(c[i-1][j]>c[i][j-1])?c[i-1][j]:c[i][j-1];
}
return c[lenA][lenB];
}
(2)字符串是连续的
法一:动态规划
字符串连续,也可以用动态规划解决,通式
x[i][j]=x[i-1][j-1]+1 if A[i]==B[j]
x[i][j]=0 if A[i]!=B[j]
代码实现:
View Code
#include <iostream>
#include <stack>
using namespace std;
const int N=100;
int c[N][N]; //c[i][j]记录公共子串中包含A[i]和B[j],并以此结尾的公共子串的长度
void LCS(char *A,char *B,int lenA,int lenB,int &tagA,int &tagB);
// tagA表示公共最长子串在字符串A中的结束的位置,tagB表示公共最长子串在字符串B中的结束的位置
void main()
{
char A[8]={'','a','b','c','b','d','a','b'};
char B[7]={'','b','d','c','a','b','c'};
int lenA=7,lenB=6,tagA=0,tagB=0;
LCS(A,B,lenA,lenB,tagA,tagB);
cout<<tagA<<""<<tagB<<""<<c[tagA][tagB]<<endl;
for(int i=tagA-c[tagA][tagB]+1;i<=tagA;i++)
cout<<A[i]<<"";
cout<<endl;
return;
}
void LCS(char *A,char *B,int lenA,int lenB,int &tagA,int &tagB)
{
int i,j;
int max=0;
memset(c,0,100*100);
for(i=0;i<=lenA;i++)
c[i][0]=0;
for(j=0;j<=lenB;j++)
c[0][j]=0;
for(i=1;i<=lenA;i++)
for(j=1;j<=lenB;j++)
{
if(A[i]==B[j])
c[i][j]=c[i-1][j-1]+1;
else
c[i][j]=0;
if(c[i][j]>max) //记录最长的长度,及各自结束的下标
{
max=c[i][j];
tagA=i;
tagB=j;
}
}
return;
}
法二:广义后缀树
用动态规划实现,时间复杂度比较高,若有多个字符串共同求最长公共子序列,则时间复杂度是个问题。可以用广义后缀树,可降低时间复杂度,但空间复杂度提高,相当于用空间换取时间。关于广义后缀树前面章节已讲过。用广义后缀树实现,需要对广义后缀树进行预处理,树中每个节点要记录以该节点为根的树的叶子的个数,假设有n个字符串,则找出所有叶子数目为n的节点,再在其中找出到根最远的那个节点,其所对应的路径就是最长公共子序列。