bzoj 4275: [ONTAK2015]Badania naukowe 动态规划

题意

给定三个数字串A,B,C,请找到一个A,B的最长公共子序列,满足C是该子序列的子串。
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;
}

猜你喜欢

转载自blog.csdn.net/qq_33229466/article/details/80973150