线性DP(中)

线性DP(中)

1.经典例题(3):最长公共子序列

一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切地说,若给定序列 \(X=<x_1,x_2,…,x_m>\),则另一序列 \(Z= <z_1,z_2,…,z_k>\)是X的子序列是指存在一个严格递增的下标序列 \(<i_1,i_2,…,i_k>\),使得对于所有 \(j=1,2,…,k\) 有:\(X_{ij}=Zj\)

给定两个序列 \(X\)\(Y\),当另一序列 \(Z\) 既是 \(X\) 的子序列又是 \(Y\) 的子序列时,称 \(Z\) 是序列 \(X\)\(Y\) 的公共子序列。

题目描述

给定两个序列 \(X =<x_1,x_2,…,x_m>\)\(Y=<y_1,y_2,…,y_n>\)。要求找出 \(X\)\(Y\) 的一个最长公共子序列,并输出长度。

输入

共有两行。每行为一个由大写字母构成的字符串,表示序列 \(X\)\(Y\)

输出

第一行为一个非负整数。表示所求得的最长公共子序列的长度。若不存在公共子序列.则输出文件仅有一行输出一个整数0。

输入样例

ABCBDAB
BDCABA

输出样例

4

数据范围

\(1\leq\) 字符串长度 \(\leq 1000\)

提示

最长公共子串(Longest Common Substirng)和最长公共子序列(Longest Common Subsequence,LCS)的区别为:

子串是串的一个连续的部分,子序列则是从不改变序列的顺序,而从序列中去掉任意的元素而获得新的序列;

也就是说,子串中字符的位置必须是连续的,子序列则可以不必连续。字符串长度小于等于1000。

注:本题来源于信息学奥赛一本通第1265题

2.经典例题(3)思路

题目让我们求最长公共子序列,

那我们不妨把 \(f\) 数组设成:

\(f[i][j]\) 表示在第一个序列的前 \(i\) 字母中出现且在第二个序列的前 \(j\) 个字母中出现的最长公共子序列长度(默认以 \(a[i]\)\(b[j]\) 结尾)。

状态转移则可以把每一集合分成四个子集:

两个序列中都不含最后一个字母的;

只含第一个序列中最后一个字母的;

只含第二个序列中最后一个字母的;

两个序列中都含最后一个字母的。

第一种和第四种的表示好推,

第一种:\(f[i-1][j-1]\)

第四种:直接算不好算,那就先都不含最后一个字母,最后长度加1
\(f[i-1][j-1]+1\)

有人就说了:“这第二种和第三种不是很好推吗?不就是 \(f[i][j-1]\)\(f[i-1][j]\) 吗?有什么难推的?”

但真的是这样吗?

\(f[i][j-1]\)表示在第一个序列的前 \(i\) 字母中出现且在第二个序列的前 \(j-1\) 个字母中出现的最长公共子序列长度。

\(f[i-1][j]\)表示在第一个序列的前 \(i-1\) 字母中出现且在第二个序列的前 \(j\) 个字母中出现的最长公共子序列长度。

有没有发现,这两个序列是没有以同一个字母结尾的,也就是说它们跟我们要求的就不是一个东西。

但即使不以同一个字母结尾又怎样,有重叠又怎样?

用就完了!!!

注1:第一种情况可以被第二种情况和第三种情况替代,所以不用写出来。

注2:第四种情况不一定会出现,所以得加上一个特判。

3.经典例题(3)代码(注释已全部标出)

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int N=1005;
int la,lb;
char a[N],b[N];
int f[N][N];
int main(){
    scanf("%s%s",a+1,b+1);//用cin好像不太行,这个题得从下标为1的地方开始输入
    int la=strlen(a+1),lb=strlen(b+1);//求长度 
    for(int i=1;i<=la;i++)
        for(int j=1;j<=lb;j++){
            f[i][j]=max(f[i-1][j],f[i][j-1]);//状态转移方程
            if(a[i]==b[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1);//特判
        }
    printf("%d",f[la][lb]);
    return 0;
}

完awa~

如果觉得还行就点个赞吧,您的支持是本蒟蒻最大的动力。

posted @ 2022-08-04 11:39  Rainforests  阅读(38)  评论(0编辑  收藏  举报