一、问题描述
如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,
则字符串一称之为字符串二的子串。
注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中。
请编写一个函数,输入两个字符串,求它们的最长公共子串,并打印出最长公共子串。
例如:输入两个字符串BDCABA和ABCBDAB,字符串BCBA和BDAB都是是它们的最长公共子序列,则输出它们的长度4,并打印任意一个子序列。
二、最长公共子序列的结构
最长公共子序列的结构有如下表示:
设序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的一个最长公共子序列Z=<z1, z2, …, zk>,则:
- 若xm=yn,则zk=xm=yn且Zk-1是Xm-1和Yn-1的最长公共子序列;
- 若xm≠yn且zk≠xm ,则Z是Xm-1和Y的最长公共子序列;
- 若xm≠yn且zk≠yn ,则Z是X和Yn-1的最长公共子序列。
其中Xm-1=<x1, x2, …, xm-1>,Yn-1=<y1, y2, …, yn-1>,Zk-1=<z1, z2, …, zk-1>。
三、子问题的递归结构
由最长公共子序列问题的最优子结构性质可知,要找出X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的最长公共子序列,可按以下方式递归地进行:当xm=yn时,找出Xm-1和Yn-1的最长公共子序列,然后在其尾部加上xm(=yn)即可得X和Y的一个最长公共子序列。当xm≠yn时,必须解两个子问题,即找出Xm-1和Y的一个最长公共子序列及X和Yn-1的一个最长公共子序列。这两个公共子序列中较长者即为X和Y的一个最长公共子序列。
由此递归结构容易看到最长公共子序列问题具有子问题重叠性质。例如,在计算X和Y的最长公共子序列时,可能要计算出X和Yn-1及Xm-1和Y的最长公共子序列。而这两个子问题都包含一个公共子问题,即计算Xm-1和Yn-1的最长公共子序列。
与矩阵连乘积最优计算次序问题类似,我们来建立子问题的最优值的递归关系。用c[i,j]记录序列Xi和Yj的最长公共子序列的长度。其中Xi=<x1, x2, …, xi>,Yj=<y1, y2, …, yj>。当i=0或j=0时,空序列是Xi和Yj的最长公共子序列,故c[i,j]=0。其他情况下,由定理可建立递归关系如下:
四、算法实现
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
enum Direction{Init=0,Up,LeftUp,Left};
void PrintLCS(int **LCS_Direct,char *pStr1,char *pStr2,int row,int col)
{
if(pStr1 == NULL || pStr2 == NULL)
return;
size_t length1 = strlen(pStr1);
size_t length2 = strlen(pStr2);
if(length1 == 0 || length2 == 0 || !(row < length1+1 && col < length2+1))
return;
if(LCS_Direct[row][col]==LeftUp)
{
if(row>1&&col>1)
{
PrintLCS(LCS_Direct,pStr1,pStr2,row-1,col-1);
}
cout<<pStr2[col-1];
}
else if(LCS_Direct[row][col]==Up)
{
if(row>1)
{
PrintLCS(LCS_Direct,pStr1,pStr2,row-1,col);
}
}
else if(LCS_Direct[row][col]==Left)
{
if(col>1)
{
PrintLCS(LCS_Direct,pStr1,pStr2,row,col-1);
}
}
}
int LCS(char *pStr1,char *pStr2)
{
if(!pStr1||!pStr2)
{
return 0;
}
size_t strLen1 = strlen(pStr1);
size_t strLen2 = strlen(pStr2);
if(strLen1==0||strLen2==0)
{
return 0;
}
//创建并初始化记录最大长度的矩阵
int i,j;
int **LCS_Length;
LCS_Length = (int**)(new int[strLen1+1]);
for(i=0;i<strLen1+1;i++)
{
LCS_Length[i] = (int*)new int[strLen2+1];
}
for(i=0;i<strLen1+1;i++)
{
for(j=0;j<strLen2+1;j++)
{
LCS_Length[i][j] = 0;
}
}
//创建并初始化记录方向的矩阵
int **LCS_Direct;
LCS_Direct = (int**)(new int[strLen1+1]);
for(i=0;i<strLen1+1;i++)
{
LCS_Direct[i] =new int[strLen2+1];
}
for(i=0;i<strLen1+1;i++)
{
for(j=0;j<strLen2+1;j++)
{
LCS_Direct[i][j] = Init;
}
}
//构建最优解矩阵
for(i=0;i<strLen1;i++)
{
for(j=0;j<strLen2;j++)
{
if(pStr1[i]==pStr2[j])
{
LCS_Length[i+1][j+1]=LCS_Length[i][j]+1;
LCS_Direct[i+1][j+1]=LeftUp;
}
else if(LCS_Length[i][j+1]>LCS_Length[i+1][j])
{
LCS_Length[i+1][j+1]=LCS_Length[i][j+1];
LCS_Direct[i+1][j+1]=Up;
}
else
{
LCS_Length[i+1][j+1]=LCS_Length[i+1][j];
LCS_Direct[i+1][j+1]=Left;
}
}
}
for(i=0;i<strLen1+1;i++)
{
for(j=0;j<strLen2+1;j++)
{
cout<<LCS_Length[i][j];
}
cout<<endl;
}
PrintLCS(LCS_Direct,pStr1,pStr2,strLen1,strLen2);
return LCS_Length[strLen1][strLen2];
}
int main(int argc,char *argv[])
{
char *pStr1="ABCBDAB";
char *pStr2="BDCABA";
int len=LCS(pStr1,pStr2);
cout<<"最大长度:"<<len<<endl;
system("pause");
return 0;
}