[GDOI2016]最长公共子串

题目大意

题目描述
数据范围


题目分析

Algorithm Alpha

将母串分成许多个块,每个块内所有位置都可以互相调换。
那么一个字符串能成功匹配一个块当且仅当其每个字母出现次数都不比块内该字母出现次数多。
我们处理 fi,j ,表示从匹配串第 i 个位置开始,匹配从第j个块开始的块,最多能匹配多少个完整的块。然后 prei,j suci,j 则是向前向后匹配第 j 个块(只匹配该块),最多能匹配多少个字符。这三个数组怎么预处理就由读者自行脑补了。
利用这三个数组,我们就可以扫一遍做答案了。
时间复杂度O(n2)

Algorithm Beta

听说在基础 LCS dp 上稍作改动即可得到这题简便解法,代码复杂度极低。我没有仔细思考,就交给感兴趣的读者脑补了。
时间复杂度 O(n2)


代码实现

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <cmath>

using namespace std;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch))
    {
        if (ch=='-')
            f=-1;
        ch=getchar();
    }
    while (isdigit(ch))
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}

const int N=2050;
const int Q=100050+N;
const int C=26;

struct Query
{
    int l,r;
}qs[Q];

bool operator<(Query a,Query b)
{
    return a.l<b.l||a.l==b.l&&a.r<b.r;
}

int f[N][N],g[N][N],pre[N][N],suc[N][N];
int cnt[N][C],app[N][C];
int len[N],st[N],en[N];
int n,m,q,tot,ans;
char S[N],T[N];

void calc()
{
    int wrg[N];
    memset(app,0,sizeof app);
    memset(wrg,0,sizeof wrg);
    for (int i=m-1;i>=0;i--)
        for (int j=0;j<tot;j++)
        {
            int c=T[i]-'a';
            if (app[j][c]++==cnt[j][c]) wrg[j]++;
            if (i+len[j]<m)
            {
                c=T[i+len[j]]-'a';
                if (--app[j][c]==cnt[j][c]) wrg[j]--;
            }
            if (i+len[j]>m) continue;
            if (!wrg[j])
                f[i][j]=f[i+len[j]][j+1]+len[j],g[i][j]=g[i+len[j]][j+1]+1;
        }
    memset(app,0,sizeof app);
    for (int i=0;i<tot;i++) wrg[i]=-1;
    for (int i=0;i<m;i++)
        for (int j=0;j<tot;j++)
        {
            int c=T[i]-'a';
            if (app[j][c]++==cnt[j][c])
            {
                while (T[wrg[j]+1]!=T[i])
                    c=T[++wrg[j]]-'a',app[j][c]--;
                c=T[++wrg[j]]-'a',app[j][c]--;
            }
            pre[i][j]=i-wrg[j];
        }
    memset(app,0,sizeof app);
    for (int i=0;i<tot;i++) wrg[i]=m;
    for (int i=m-1;i>=0;i--)
        for (int j=0;j<tot;j++)
        {
            int c=T[i]-'a';
            if (app[j][c]++==cnt[j][c])
            {
                while (T[wrg[j]-1]!=T[i])
                    c=T[--wrg[j]]-'a',app[j][c]--;
                c=T[--wrg[j]]-'a',app[j][c]--;
            }
            suc[i][j]=wrg[j]-i;
        }
}

void getans()
{
    ans=0;
    for (int i=0;i<m;i++)
        for (int j=0;j<tot;j++)
        {
            int tmp=f[i][j],h=g[i][j];
            if (i&&j) tmp+=pre[i-1][j-1];
            tmp+=suc[i+f[i][j]][j+h];
            ans=max(ans,tmp);
        }
}

int main()
{
    freopen("lcs.in","r",stdin);
    freopen("lcs.out","w",stdout);
    scanf("%s",T),m=strlen(T);
    scanf("%s",S),n=strlen(S);
    q=read();
    for (int i=1;i<=q;i++)
        qs[i].l=read(),qs[i].r=read();
    for (int i=0;i<n;i++)
        qs[++q].l=i,qs[q].r=i;
    sort(qs+1,qs+1+q);
    tot=0;
    int lc=qs[1].l,rc=qs[1].r;
    for (int i=2;i<=q;i++)
    {
        if (qs[i].l>rc)
        {
            st[tot]=lc,en[tot]=rc;
            len[tot++]=rc-lc+1;
            lc=qs[i].l,rc=0;
        }
        rc=max(qs[i].r,rc);
    }
    st[tot]=lc,en[tot]=rc;
    len[tot++]=rc-lc+1;
    for (int i=0;i<tot;i++)
        for (int j=st[i];j<=en[i];j++)
            cnt[i][S[j]-'a']++;
    calc();
    getans();
    printf("%d\n",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值