线性DP(中)

线性DP(中)

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

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

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

题目描述

给定两个序列 X=<x1,x2,,xm>Y=<y1,y2,,yn>。要求找出 XY 的一个最长公共子序列,并输出长度。

输入

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

输出

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

输入样例

ABCBDAB
BDCABA

输出样例

4

数据范围

1 字符串长度 1000

提示

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

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

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

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

2.经典例题(3)思路

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

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

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

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

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

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

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

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

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

第一种:f[i1][j1]

第四种:直接算不好算,那就先都不含最后一个字母,最后长度加1
f[i1][j1]+1

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

但真的是这样吗?

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

f[i1][j]表示在第一个序列的前 i1 字母中出现且在第二个序列的前 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 @   Rainforests  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示