问题描述:求Z={'A','B','C','B','D','A','B'},X={{'B','D','C','A','B','A'}}的最长公共子序列
注:最长公共子序列不同于子串,它不是连续的;
我们设连个指针,分别指向这两个序列的末尾,当末尾两个元素的值相等的时候说明这个元素属于这个最长子序列
当不相等的时候我们先去掉一个序列 的末尾元素然后查询去掉这个元素后的最长公共子序列的长度,然后去掉另一个序列的末尾,做相同的工作取其中一个最大的
。解释一下为什么会这样做(以下是自己理解的白话,可能有误)。
假设有两个序列A={adfs};B={dacd};当指针指向的元素相同的时候这个元素一定在最长的序列里这个容易理解。我们分别去掉最后一个元素进行查询剩下的序列的最长公共子序列的
长度是做了两次试探,当指针同时指向最后一个元素s和d的时候我们发现连个元素是不同的,这时候有三种情况1.两个元素不都在最长公共子序列里面2.s在最长子序列里面,3.d在最长
公共子序列里面,不可能同时在里面如果同时在里面的那么这两个元素一定是相等的;
所以我们可以得到递推式
递归:
#include<stdio.h>
#include<stdlib.h>
int compute(int s[8][7],char s1[],char s2[],int h,int k)
{
if(h<0||k<0)
return 0;
int i,j;
int count=0;
for(j=0;j<7;j++)
{
s[0][j]=0;
}
for(i=0;i<8;i++)
{
s[i][0]=0;
}
if(s1[h]==s2[k])
{
i=h;
j=k;
count= compute(s,s1,s2,--h,--k)+1;
s[i+1][j+1]=count;
}
else{
i=h;
j=k;
int count1=compute(s,s1,s2,h,--k);
int count2=compute(s,s1,s2,--i,j);
count= count1>count2?count1:count2;
s[i+2][j+1]=count;
}
return count;
}
void printres(int s[8][7],char s2[],int i,int j)
{
if(i<=0||j<=0)
{
return ;
}
if(s[i-1][j]!=s[i][j]&&s[i][j-1]!=s[i][j])
{
printres(s,s2,--i,--j);
printf("%c",s2[j]);
}else if(s[i-1][j]==s[i][j])
{
printres(s,s2,--i,j);
}else
{
printres(s,s2,i,--j);
}
}
void main()
{
char s1[7]={'A','B','C','B','D','A','B'};
char s2[6]={'B','D','C','A','B','A'};
int s[8][7];
printf("the max common string length is:%d\n",compute(s,s1,s2,6,5));
for(int i=0;i<8;i++)
{
for(int j=0;j<7;j++)
{
if(s[i][j]<0)
printf("-1\t");
else
printf("%d\t",s[i][j]);
}
printf("\n");
}
printf("===============================\n");
printres(s,s2,7,6);
printf("\n");
}
图片:
从图中打印的二维数组可以看到递归过程中并不是对每一种情况进行了运算而是有选择的进行递归但,即使这样也做了大量的重复的运算,不推荐使用这种方法,我觉得我们可以把它改成备忘录方法
动态规划(推荐)
#include<stdio.h>
#include<stdlib.h>
void compute(int s[8][7],char s1[],char s2[],int h,int k)
{
int i,j;
for(j=0;j<7;j++)
{
s[0][j]=0;
}
for(i=0;i<8;i++)
{
s[i][0]=0;
}
for(i=1;i<=h;i++)
{
for(j=1;j<=k;j++)
{
if(s1[i-1]==s2[j-1])
{
s[i][j]=s[i-1][j-1]+1;
}else
{
if(s[i-1][j]>s[i][j-1])
{
s[i][j]=s[i-1][j];
}else
{
s[i][j]=s[i][j-1];
}
}
}
}
}
void printres(int s[8][7],char s2[],int i,int j)
{
if(i<=0||j<=0)
return ;
if(s[i-1][j]!=s[i][j]&&s[i][j-1]!=s[i][j])
{
printres(s,s2,--i,--j);
printf("%c",s2[j]);
}else if(s[i-1][j]==s[i][j])
{
printres(s,s2,--i,j);
}else
{
printres(s,s2,i,--j);
}
}
void main()
{
char s1[7]={'A','B','C','B','D','A','B'};
char s2[6]={'B','D','C','A','B','A'};
int s[8][7];
compute(s,s1,s2,7,6);
printf("the max common string length is:%d\n",s[7][6]);
for(int i=0;i<8;i++)
{
for(int j=0;j<7;j++)
{
if(s[i][j]<0)
printf("-1\t");
else
printf("%d\t",s[i][j]);
}
printf("\n");
}
printf("===============================\n");
printres(s,s2,7,6);
printf("\n");
}
结果: