题意
给定三个数字串A,B,C,请找到一个A,B的最长公共子序列,满足C是该子序列的子串。
n,m,k≤3000
n
,
m
,
k
≤
3000
分析
既然题目要求C必须是子序列的子串,那么我们可以分别枚举C在A和B的哪个位置开始出现,显然最优的结束位置必然是从开始位置开始在子序列DAG上跳,预处理出来跳到哪里后,剩下的部分就是一个前缀LCS和后缀LCS,分别预处理即可。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
const int N=3005;
int n,m,k,a[N],b[N],c[N],pre[N][N],suf[N][N],ls[1005],nx[N][1005],to1[N],to2[N];
void prework()
{
for (int i=1;i<=1000;i++) ls[i]=0;
for (int i=n;i>=1;i--)
{
for (int j=1;j<=1000;j++) nx[i][j]=ls[j];
ls[a[i]]=i;
}
for (int i=1;i<=n;i++)
{
int x=i;
for (int j=1;j<=k&&x;j++) x=nx[x][c[j]];
to1[i]=x;
}
for (int i=1;i<=1000;i++) ls[i]=0;
for (int i=m;i>=1;i--)
{
for (int j=1;j<=1000;j++) nx[i][j]=ls[j];
ls[b[i]]=i;
}
for (int i=1;i<=m;i++)
{
int x=i;
for (int j=1;j<=k&&x;j++) x=nx[x][c[j]];
to2[i]=x;
}
}
void dp()
{
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
pre[i][j]=(a[i]==b[j]?pre[i-1][j-1]+1:std::max(pre[i-1][j],pre[i][j-1]));
for (int i=n;i>=1;i--)
for (int j=m;j>=1;j--)
suf[i][j]=(a[i]==b[j]?suf[i+1][j+1]+1:std::max(suf[i+1][j],suf[i][j+1]));
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
scanf("%d",&m);
for (int i=1;i<=m;i++) scanf("%d",&b[i]);
scanf("%d",&k);
for (int i=1;i<=k;i++) scanf("%d",&c[i]);
prework();
dp();
int ans=(k?-1:0);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
if (!to1[i]||!to2[j]) continue;
ans=std::max(ans,k+pre[i][j]+suf[to1[i]+1][to2[j]+1]);
}
printf("%d",ans);
return 0;
}