bzoj 2806: [Ctsc2012]Cheat广义SAM+二分+dp+单调队列

Posted lokiii

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 2806: [Ctsc2012]Cheat广义SAM+二分+dp+单调队列相关的知识,希望对你有一定的参考价值。

把模板串建一个广义SAM
然后在线查询,每次在SAM上预处理出一个a[i]表示i位置向前最多能匹配多长的模板串
二分答案L,dp判断,设f[i]为·~i有几个匹配,转移显然是f[i]=max{f[i-1],f[j]+i-j(i-a[i]<=j<=i-L)},根据性质,i-a[i]是单调的(或者直接看a[i]的预处理过程也能看出来),所以这个dp可以用单调队列优化转移,最后判断是否f[n]>=L*0.9

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=3000005;
int n,m,fa[N],ch[N][2],dis[N],cur=1,con=1,la,a[N],q[N],f[N];
char s[N];
void ins(int c,int id)
{
    la=cur,dis[cur=++con]=id;
    int p=la;
    for(;p&&!ch[p][c];p=fa[p])
        ch[p][c]=cur;
    if(!p)
        fa[cur]=1;
    else
    {
        int q=ch[p][c];
        if(dis[q]==dis[p]+1)
            fa[cur]=q;
        else
        {
            int nq=++con;
            dis[nq]=dis[p]+1;
            memcpy(ch[nq],ch[q],sizeof(ch[nq]));
            fa[nq]=fa[q];
            fa[q]=fa[cur]=nq;
            for(;ch[p][c]==q;p=fa[p])
                ch[p][c]=nq;
        }
    }
}
bool ok(int w,int n)
{
    int l=1,r=0;//cerr<<w<<endl;
    for(int i=1;i<=n;i++)
    {
        f[i]=f[i-1];
        while(l<=r&&q[l]<i-a[i])
            l++;
        if(l<=r)
            f[i]=max(f[i],f[q[l]]+i-q[l]);
        if(i-w+1>=0)
        {
            while(l<=r&&f[q[r]]-q[r]<=f[i-w+1]-(i-w+1))
                r--;
            q[++r]=i-w+1;
        }//cerr<<i<<" "<<f[i]<<endl;
    }
    return f[n]*10>=n*9;
}
int main()
{
    scanf("%d%d",&m,&n);
    for(int j=1;j<=n;j++)
    {
        scanf("%s",s+1);
        cur=1;
        for(int i=1,len=strlen(s+1);i<=len;i++)
            ins(s[i]-‘0‘,i);
    }
    while(m--)
    {
        scanf("%s",s+1);
        int len=strlen(s+1);
        for(int i=1,l=0,p=1;i<=len;i++)
        {
            int c=s[i]-‘0‘;
            if(ch[p][c])
                l++,p=ch[p][c];
            else
            {
                for(;p&&!ch[p][c];p=fa[p]);
                if(!p)
                    l=0,p=1;
                else
                    l=dis[p]+1,p=ch[p][c];
            }
            a[i]=l;//cerr<<i<<" "<<a[i]<<endl;
        }
        int l=0,r=len,ans=0;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(ok(mid,len))
                l=mid+1,ans=mid;
            else
                r=mid-1;
        }
        printf("%d
",ans);
    }
    return 0;
}


以上是关于bzoj 2806: [Ctsc2012]Cheat广义SAM+二分+dp+单调队列的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ2806: [Ctsc2012]Cheat

bzoj 2806: [Ctsc2012]Cheat

[bzoj2806][Ctsc2012]Cheat(后缀自动机(SAM)+二分答案+单调队列优化dp)

BZOJ2806[Ctsc2012]Cheat 广义后缀自动机+二分+单调队列优化DP

[BZOJ2806][CTSC2012]熟悉的文章(Cheat)

BZOJ2806CTSC2012Cheat 广义后缀自动机+二分+Dp