线性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~
如果觉得还行就点个赞吧,您的支持是本蒟蒻最大的动力。