最长公共子序列与最长公共子串以及他们的打印(dp)

来源:互联网 发布:windows pe如何分区 编辑:程序博客网 时间:2024/05/01 17:59

1.最长公共子序列:两个序列都出现过并且出现顺序与母串保持一致,我们将其称为公共子序列,最长公共子序列(Longest Common Subsequence, LCS)是其中最长的。


c[i,j]=0c[i1,j1]+1max(c[i,j1],c[i1,j])i=0 or j=0i,j>0 and  xi=yji,j>0 and xiyj

两字符相等时左上角的数加一,否则取左边和上边最大的;

寻找方法:以图说明



例子:

abcfbc abfcabprogramming contest abcd mnp
下面是代码:(打印路径的方法)

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;char a[1000],b[1000];//两个序列int dp[1000][1000];//记录序列的长度struct node{    char q;//当前点的字符    int x,y;//上一个点的坐标} cun[1000][1000];//记录每个点的路径char cun2[1000];//存储路径int main(){    while(~scanf("%s%s",a+1,b+1))//不从零开始,方便操作    {        memset(dp,0,sizeof(dp));        int i,j;        int la=strlen(a+1),lb=strlen(b+1);        for(i=1; i<=lb; i++)//开始动归        {            for(j=1; j<=la; j++)            {                if(a[j]==b[i])//两个字符相等时,记录左上角的坐标和当前的字符                    dp[i][j]=dp[i-1][j-1]+1,cun[i][j].x=i-1,cun[i][j].y=j-1,cun[i][j].q=a[j];                else if(dp[i-1][j]>dp[i][j-1])//记录左方和上方中最大的数据                {                    dp[i][j]=dp[i-1][j];                    cun[i][j].x=cun[i-1][j].x,cun[i][j].y=cun[i-1][j].y,cun[i][j].q=cun[i-1][j].q;                }                else if(dp[i-1][j]==dp[i][j-1])//左方和上方相等时,记录上方的数据                {                    dp[i][j]=dp[i][j-1];                    cun[i][j].x=cun[i-1][j].x,cun[i][j].y=cun[i-1][j].y,cun[i][j].q=cun[i-1][j].q;                }                else                {                    dp[i][j]=dp[i][j-1];                    cun[i][j].x=cun[i][j-1].x,cun[i][j].y=cun[i][j-1].y,cun[i][j].q=cun[i][j-1].q;                }            }        }        printf("%d\012",dp[lb][la]);        int xx=lb,yy=la,zz;        for(i=0;i<dp[lb][la];i++)//开始回溯找路径        {            cun2[i]=cun[xx][yy].q;            zz=xx;            xx=cun[xx][yy].x;            yy=cun[zz][yy].y;        }        for(i=dp[lb][la]-1;i>=0;i--)            printf("%c ",cun2[i]);        printf("\n");        memset(a,0,sizeof(a));        memset(b,0,sizeof(b));    }    return 0;}


2.最长公共子串:子串是要求更严格的一种子序列,要求在母串中连续地出现。

c[i,j]=0c[i1,j1]+10i=0 or j=0xi=yjxiyj
只有字符相同时,才加一,其它情况都是零;


自己制作的图(可能不太好)



#include<stdio.h>#include<string.h>char a[1000],b[1000];//存储两个序列int dp[1000][1000];//子串的长度int main(){    while(~scanf("%s%s",a+1,b+1))    {        memset(dp,0,sizeof(dp));        int i,j;        int max=0;//记录最长的长度        int xx;//记录最长的子串的最后一个字符的坐标,因为子串是连续的所以找哪一个都一样,连续是很重要的        int la=strlen(a+1),lb=strlen(b+1);        for(i=1; i<=lb; i++)//开始动归        {            for(j=1; j<=la; j++)            {                if(a[j]==b[i])                {                    dp[i][j]=dp[i-1][j-1]+1;                    if(max<dp[i][j])//记录长度和坐标                    {                        max=dp[i][j];                        xx=i;                    }                }                else                    dp[i][j]=0;            }        }        printf("%d\012",max);        for(i=xx-max+1; i<=xx; i++)//因为是连续的,所以往后退max-1个就是它的开头;            printf("%c ",b[i]);        printf("\n");        memset(a,0,sizeof(a));        memset(b,0,sizeof(b));    }    return 0;}

有任何意见和好的方法,请留言,谢谢!


0 0
原创粉丝点击