最长公共子序列:一个数列 S,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。子序列不要求是连续的,但要求前后的相对顺序一致!
使用DP动态规划方法,算法时间、空间复杂度为O(m*n)。
递推公式:
既然涉及到公共子序列,也就是有X的第 i 个字符和Y的第 j 个字符相等的情况。
显然如果X[i] = Y[j] 那么长度分别为 i 和 j 的最长公共子序列就是长度分别为 i-1 和 j-1的最长公共子序列 加上 X[i] 或 Y[j]。
如果X[i] != Y[j] 呢?
如果不相等,那么长度为 i 和长度为 j 的序列的最长公共子序列就是“长度为i-1 和 j ” 和“长度为 i 和 j-1 ”中最长公共子序列中较长的一个。
我的程序:
int max(int a,int b)
{
return a>b?a:b;
}
int main()
{
char a[100],b[100];
while(cin>>a>>b)
{
int na=strlen(a);
int nb=strlen(b);
int len[100][100];
memset(len,0,sizeof(len));
for(int i=0;i<na;++i)
{
for(int j=0;j<nb;++j)
{
if(i==0 || j==0)//ij为0时必须小心处理。如果只考虑a[i]==b[j]情况,当不相等时,就会调用max(a[i-1],b[j-1])就越界了。
{
if(a[i]==b[j])
{
len[i][j]=1;
}
else if(i==0 && j!=0)
{
len[i][j]=len[i][j-1];
}
else if(i!=0 && j==0)
{
len[i][j]=len[i-1][j];
}
else if(i==0 && j==0)
{
len[i][j]=0;
}
}
else if(a[i]==b[j])
{
len[i][j]=len[i-1][j-1]+1;
}
else
{
len[i][j]=max(len[i-1][j],len[i][j-1]);
}
}
}
cout<<len[na-1][nb-1]<<endl;
}
return 0;
}
最大子串的输出:
由于每次调用至少向上或向左(或向上向左同时)移动一步,故最多调用(m + n)次就会遇到i = 0或j = 0的情况,此时开始返回。返回时与递归调用时方向相反,步数相同,故算法时间复杂度为Θ(m + n)。
int i=na-1,j=nb-1,count=len[na-1][nb-1];
while(count!=0)
{
if(a[i]==b[j])
{
cout<<a[i]<<" ";
i--;
j--;
count--;
}
else if(len[i][j-1]>len[i-1][j])
{
j--;
}
else
{
i--;
}
}cout<<endl;
从最后开始,碰到一样的输出,不一样的,往更大的方向跑。其实就是前面生成len的逆过程。