BZOJ 2806: [Ctsc2012]Cheat 後綴自動機+單調隊列優化DP

題意:算了不想寫題意了233
題解:對所有串建一個廣義後綴自動機,然後對於每個模板串算出到第i位最大能匹配多遠,二分出一個答案,然後寫出一個DP方程,F[i]表示到第i位最多能匹配多少位,f[i]=max(f[j] (i-maxx[i]<=j<=i-midans) +i-j) 顯然單調隊列亂搞一下就行了。

#include<iostream>
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<algorithm>
using namespace std;
#define eps 1e-8
int n,m;
char s[2000000];
struct sam
{
    sam *son[2];
    sam *parent;
    int max_len;
    sam(int _):max_len(_)
    {
        son[0]=son[1]=parent=0x0;
    }
}*root=new sam(0),*last=root;
void Insert(int zm)
{
    sam *p=last;
    sam *np=new sam(p->max_len+1);
    while(p && !p->son[zm])
    {
        p->son[zm]=np;
        p=p->parent;
    }
    if(!p) np->parent=root;
    else
    {
        sam *q=p->son[zm];
        if(q->max_len==p->max_len+1) np->parent=q;
        else
        {
            sam *nq=new sam(p->max_len+1);
            nq->parent=q->parent;
            memcpy(nq->son,q->son,sizeof(nq->son));
            np->parent=nq;
            q->parent=nq;
            while(p && p->son[zm]==q) p->son[zm]=nq,p=p->parent;
        }
    }
    last=np;
}
int maxx[2000000];
int f[2000000];
int len;
struct point
{
    int id,val;
    point(){}
    point(int _,int __):id(_),val(__){}
};
bool check(int k)
{
    static point q[2000000];
    int s=1,t=1;
    for(int i=0;i<k;i++) f[i]=0;
    for(int i=k;i<=len;i++)
    {
        while(s<t && f[i-k]-(i-k)>q[t-1].val) t--;
        q[t++]=point(i-k,f[i-k]-(i-k));
        f[i]=f[i-1];
        while(s<t && i-q[s].id>maxx[i]) s++;
        if(s<t) f[i]=max(f[i],q[s].val+i);
    }
    double rate=f[len];
    rate/=(double)len;
    return rate>=0.9-eps;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",s+1);
        len=strlen(s+1);
        last=root;
        for(int j=1;j<=len;j++) Insert(s[j]-'0');
    }
    for(int hh=1;hh<=n;hh++)
    {
        scanf("%s",s+1);
        len=strlen(s+1);
        int fix=0;
        sam *o=root;
        for(int i=1;i<=len;i++)
        {
            if(o->son[s[i]-'0']) o=o->son[s[i]-'0'],fix++;
            else
            {
                while(o && !o->son[s[i]-'0']) o=o->parent;
                if(!o)
                {
                    fix=0;
                    o=root;
                }
                else
                {
                    fix=o->max_len;
                    o=o->son[s[i]-'0'];
                    fix++;
                }
            }
            maxx[i]=fix;
        }
        int jilu=0;
        int l=1,r=len;
        while(l<=r)
        {
            int mid=l+r>>1;
            if(check(mid)) jilu=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%d\n",jilu);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章