经典dp 最长公共子序列
Posted 可是我不配
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了经典dp 最长公共子序列相关的知识,希望对你有一定的参考价值。
首先,说明一下子序列的定义……
一个序列A={a1,a2,a3,...,an},从中删除任意若干项,剩余的序列叫A的一个子序列。
很明显(并不明显……),子序列……并不需要元素是连续的……(一开始的时候思维总是以为元素是连续的,好傻啊……)
然后是公共子序列……
如果C是A的子序列,也是B的子序列,那么C是A和B的公共子序列……
公共子序列一般不止一个,最长的那个就是最长公共子序列,当然也可能不止一个……
煮个栗子……
A={1,3,6,9,5,4,8,7},B={1,6,3,4,5,7}
{1,4,7}是A和B的公共子序列
{1,3,4,7}是A和B的最长公共子序列
好了,说明的部分就到这,接下来,进入解决问题的部分……
给出序列A={a1,a2,a3...an},B={b1,b2,b3...bn}
我们用lcs[i][j]来表示A的前i项和B的前j项的最长公共子序列的长度
flag[i][j]表示这一点是由哪点继承来的,然后,开始搜索……
如果……
(1)A[i]==B[j]
那么就代表lcs[i][j]的最后一项一定是A[i],就是在lcs[i-1][j-1]接上A[i],也就是lcs[i][j]=lcs[i-1][j-1]+1,并记录flag[i][j]
(2)A[i]!=B[j]
那么lcs[i][j]=max(lcs[i-1][j-1],lcs[i][j-1]),因为既然接不上,那就继承一个长一点的留着呗,不要忘记了flag[i][j]的数字是不同的亲~
可能我说的比较简略,上一张比较好理解的图……
可能第一次看会很凌乱,仔细看吧亲……
最后按flag打出来就好了~
最后上代码
1 #include<stdio.h> 2 #include<string.h> 3 int lcs[1005][1005]; 4 int flag[1005][1005]; 5 char a[1005],b[1005]; 6 void findlcs(char a[],char b[],int lena,int lenb){ 7 int i,j; 8 for(i=1;i<=lena;i++) 9 for(j=1;j<=lenb;j++){ 10 if(a[i-1]==b[j-1]){ 11 lcs[i][j]=lcs[i-1][j-1]+1; 12 flag[i][j]=1; 13 } 14 else{ 15 if(lcs[i][j-1]>lcs[i-1][j]){ 16 lcs[i][j]=lcs[i][j-1]; 17 flag[i][j]=2; 18 } 19 else{ 20 lcs[i][j]=lcs[i-1][j]; 21 flag[i][j]=3; 22 } 23 } 24 } 25 } 26 void printlcs(char a[],char b[],int lena,int lenb){ 27 int i=lena; 28 int j=lenb; 29 int len=0; 30 int rec[1050]; 31 while(i>0&&j>0){ 32 if(flag[i][j]==1){ 33 rec[len++]=a[i-1]; 34 i--,j--; 35 } 36 else if(flag[i][j]==2) j--; 37 else if(flag[i][j]==3) i--; 38 } 39 len--; 40 while(len>=0){ 41 printf("%c",rec[len--]); 42 } 43 printf("\\n"); 44 } 45 int main(){ 46 while(~scanf("%s%s",a,b)){ 47 int lena=strlen(a); 48 int lenb=strlen(b); 49 findlcs(a,b,lena,lenb); 50 printlcs(a,b,lena,lenb); 51 memset(flag,0,sizeof(flag)); 52 memset(lcs,0,sizeof(lcs)); 53 } 54 return 0; 55 }
以上是关于经典dp 最长公共子序列的主要内容,如果未能解决你的问题,请参考以下文章