dp专题:最长公共子序列

最长公共子序列

题目描述

一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切地说,若给定序列x1,x2,…,x,则另一序列Z1,z2,…,zk是X的子序列是指存在一个严格递增的下标序列使得对于所有j=1,2,…,k有:

X

例如,序列zB,C,D,B是序列XA,B,C,B,D,A,B的子序列,相应的递增下标序列为2,3,5,。给定两个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称z是序列x和Y的公共子序列。例如,若A,B,C,B,D,A,B>和YB,D,C,A,B,A,则序列B,C,A是X和Y的一个公共子序列,序列 B,C,B,A也是X和Y的一个公共子序列。而且,后者是X和Y的一个最长公共子序列.因为x和Y没有长度大于4的公共子序列。

给定两个序列x1,x2,…,xm和y1,y2….yn.要求找出X和Y的一个最长公共子序列。
输入

输入共有两行。每行为一个由大写字母构成的长度不超过200的字符串,表示序列X和Y。
输出

输出第一行为一个非负整数。表示所求得的最长公共子序列的长度。若不存在公共子序列.则输出文件仅有一行输出一个整数0。否则在输出文件的第二行输出所求得的最长公共子序列(也用一个大写字母组成的字符串表示)。若符合条件的最长公共子序列不止一个,只需输出其中任意的一个。
样例输入

ABCBDAB
BDCABA
样例输出

4
BCBA

题目解析:
题目求最长公共子序列,子序列是不用连续的,意思就是说
字符串abcd和 awbwcwdw 如果看连续的子序列的话那最长的为1的字符,四个长度为1的子序列,但是无需连续的话就有一个最长的子序列为4 也就是abcd;

dp一般都有一个数组储存状态,这个也有,c[i][j],二维数组,第一维表示的是第一个字符串,第二维表示的是第二个字符串,i和j的意思就是所属的字符串的位置,数组里面存的是第一个字符串中前i个字符和第二个字符串前j个字符的最长子序列的长度,就比如abcd和awbwcwdw c[1][1]=1;c[1][2]=1;c[1][3]=1;——
c[2][2]=1;c[2][3]=2—;
如果有个过程可以实现这个,那按照这个趋势到每个字符串的最后一个字符就不就是所要的答案吗,也就是二个完整的字符串的最长子序列;

在这个过程中肯定需要判断,二个字符要不是相同要不就是不相同,那就分类讨论;
首先,当我们比较二个字符串中其中一个字符时,假如他们相等,假如此时i=5 ,j=6;也就是第一个字符串的第5个字符和第二个字符串的第6个字符相同,那我们只需要知道在他们之前的状态最长子序列是多长再加上此时的1 就是这个位置的最长子序列了;
假如二个字符不相同比较的时候,那我们就需要考虑他之前的状态了,既然不相同那就是等于他之前的状态,无需加一,可以把不相同当做多余来理解,也就是说,在这个子序列中加上了这个字符,长度没有增加,但是我们在比较的时候是二个字符在比较,你怎么知道是哪个字符没有用呢,由之前的定理得,不相同的时候等于他之前的子序列长度,所以我们只需比较对于这个字符来说他的之前状态和另一个字符的之前状态,看看谁的子序列长那就可以看谁更没用了,选那个相对有用的就行了,比的时候假装那一个字符是没用的,比如i=5是没用的,那他的之前状态就是i=4,j=6时,假如另一个没用,那就是i=5.j=5,比较得到大的最为此时的状态;

另外还需初始化;当其实一个字符串是0位置的时候,都是0,因为,一个是空的怎么可能有相同的;

代码:

#include<stdio.h>
#include<string.h>
char a[1005];
char b[1005];
int c[1005][1005];
#define max(x,y) x>y?x:y

int main()
{
    scanf("%s",a);
    scanf("%s",b);
    int qq=strlen(a);
    int pp=strlen(b);   
    for(int i=0;i<=qq;i++)
        c[i][0]=0;
    for(int j=0;j<=pp;j++)
        c[0][j]=0; 

    for(int i=0;i<qq;i++)
        for(int j=0;j<pp;j++)
        {
            if(a[i]==b[j])
            {
                c[i+1][j+1]=c[i][j]+1;
            }
            else
            {
                c[i+1][j+1]=max(c[i][j+1],c[i+1][j]);
            }
        }

    if(c[qq][pp]==0)
        printf("0");
    else

        printf("%d\n",c[qq][pp]);

    char d[1005];
    int up=qq-1,down=pp-1;
    int w=c[qq][pp];
    while(w!=0)
    {
        if(a[up]==b[down])      
        {
            d[w-1]=a[up];   
            up--;
            down--;
            w--;

        }
        else
        {
            if(c[up][down+1]>c[up+1][down])
            {
                up--;
            }
            else
            if(c[up][down+1]<c[up+1][down])
            {
                down--;
            }
            else
            {
                up--;
            }   
        }
    }


        if(c[qq][pp]!=0)
        {

            d[c[qq][pp]]='\0';
            printf("%s",d);
        }
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值