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打出来就好了~

最后上代码

#include<stdio.h>
#include<string.h>
int lcs[1005][1005];
int flag[1005][1005];
char a[1005],b[1005];
void findlcs(char a[],char b[],int lena,int lenb){
    int i,j;
    for(i=1;i<=lena;i++)
        for(j=1;j<=lenb;j++){
            if(a[i-1]==b[j-1]){
                lcs[i][j]=lcs[i-1][j-1]+1;
                flag[i][j]=1;
            }
            else{
                if(lcs[i][j-1]>lcs[i-1][j]){
                    lcs[i][j]=lcs[i][j-1];
                    flag[i][j]=2;
                }
                else{
                    lcs[i][j]=lcs[i-1][j];
                    flag[i][j]=3;
                }
            }
        }
}
void printlcs(char a[],char b[],int lena,int lenb){
    int i=lena;
    int j=lenb;
    int len=0;
    int rec[1050];
    while(i>0&&j>0){
        if(flag[i][j]==1){
            rec[len++]=a[i-1];
            i--,j--;
        }
        else if(flag[i][j]==2) j--;
        else if(flag[i][j]==3) i--;
    }
    len--;
    while(len>=0){
        printf("%c",rec[len--]);
    }
    printf("\n");
}
int main(){
    while(~scanf("%s%s",a,b)){
        int lena=strlen(a);
        int lenb=strlen(b);
        findlcs(a,b,lena,lenb);
        printlcs(a,b,lena,lenb);
        memset(flag,0,sizeof(flag));
        memset(lcs,0,sizeof(lcs));
    }
    return 0;
}

上两个具体题目(题目出自buaacoding.cn)

Bamboo and the Ancient Spell

时间限制: 1000 ms 内存限制: 65536 kb

Problem description

The Hogwarts has set up a new course recently, giving you the ancient spells and requiring you to translate them into the modern ones. Every two ancient spells encode one modern spell.

Wizards and witches just point the ancient spells with the magic wands and speak loudly "THE LONGEST COMMON SUBSEQUENCE !", and they'll get the modern spell required:)

Ok, stop trying this muggles...Though we can't get the answers as quickly as they do, we can still decode them by coding :)

So Bamboo will give you a lot of ancient spells( two in a group),and hope you just return the LENGTH of the modern spell.

Attention please! The ancient spells may include strange characters '#' and '?'. They are too old to know their exact meanings, so we can just think that '#'s can not match any character while finding the answer, but '?' can match any character in the other ancient spell except '#'.

Input

The input file consists of several test cases.

Each test case includes 2 lines, one ancient spell per line.

The length of per ancient spell will less than 100 characters,and all characters are in upper case.

Output

For each case, output one line: the length of the modern spell.

sample input

ABCDE
ZBD
AC#EB
C#BFG
ACE#?
KIKI#A
???
?#?

Sample output

2
2
1
2

A TIP

emmmm, Can you guess what 'spell' means here? And what about the modern spells? Pay attention to what wizards and witches say when they use magic.

Another TIP

In case 2, '#' can not match any character, just like it doesn't exist, so the modern spell is "CB".

In case 3, '?' can match any character except '#', which means '?' can be seen as ‘A~Z' and '?' of course.

题目分析

这道题是最长公共子序列问题的一个简单改编,本质上还是一个最长公共子序列问题,只不过要在判断字符相等时加一些条件。

用lcs[i][j]表示串a的前i个字符与串b的前j个字符的最长公共子序列长度

那么,当a[i]与b[j]匹配的时候

lcs[i][j]=lcs[i-1][j-1]+1

当a[i]与b[j]不匹配的时候

lcs[i][j]=max{lcs[i-1][j-1],lcs[i][j-1]}

示例代码

#include<stdio.h>
#include<string>
#include<iostream>
#include<cstring>
using namespace std;
int lcs[1005][1005];
char a[1005],b[1005];
int len_a,len_b;
void find_lcs(char a[],char b[],int len_a,int len_b)
{
    int i,j;
    for(i=1;i<=len_a;i++)
        for(j=1;j<=len_b;j++){
        if((a[i-1]!='#'&&b[j-1]!='#')&&(a[i-1]==b[j-1]||a[i-1]=='?'||b[j-1]=='?')){
            lcs[i][j]=lcs[i-1][j-1]+1;
        }
        else {
            if(lcs[i][j-1]>lcs[i-1][j]){
                lcs[i][j]=lcs[i][j-1];
            }
            else{
                lcs[i][j]=lcs[i-1][j];
            }
        }
    }
}
int main()
{
    while(scanf("%s %s",&a,&b)!=EOF)
    {
        len_a=strlen(a);
        len_b=strlen(b);
        memset(lcs,0,sizeof(lcs));
        find_lcs(a,b,len_a,len_b);
        printf("%d\n",lcs[len_a][len_b]);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值