問題描述:求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");
}
結果: