bzoj2806 [Ctsc2012]Cheat(SAM+单调队列优化dp)

版权声明:转载附上原文地址即可~欢迎各位神犇来虐~ https://blog.csdn.net/Icefox_zhx/article/details/79952610

首先我们建出广义SAM,然后对于每一个询问串S,我们处理出len[i],表示最大的S[i-len[i]+1…i]可以被匹配。我们二分答案,dp判定,f[i]表示前i个字符最多匹配多少位。
f[i]=max{f[i1],f[j]+ij|ilen[i]<=j<=iL}
j的取值范围是随着i的增大而单调右移的,因此可以单调队列优化到O(n)

总的复杂度O(nlogn)

注意不要随便清空大数组qaq,这样会使你的复杂度炸掉的!

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define N 1000010
#define ll long long
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n=0,m,last,rt,par[N<<1],mx[N<<1],son[N<<1][2],len[N],f[N],qq[N];
char s[N];
inline void ins(int ch){
    int p=last;
    if(son[p][ch]){
        int q=son[p][ch];
        if(mx[q]==mx[p]+1){last=q;return;}
        int nq=++n;mx[nq]=mx[p]+1;last=nq;par[nq]=par[q];par[q]=nq;
        memcpy(son[nq],son[q],sizeof(son[q]));
        for(;p&&son[p][ch]==q;p=par[p]) son[p][ch]=nq;return;
    }int np=++n;mx[np]=mx[p]+1;last=np;
    for(;p&&!son[p][ch];p=par[p]) son[p][ch]=np;
    if(!p){par[np]=rt;return;}
    int q=son[p][ch];
    if(mx[q]==mx[p]+1){par[np]=q;return;}
    int nq=++n;mx[nq]=mx[p]+1;memcpy(son[nq],son[q],sizeof(son[q]));
    par[nq]=par[q];par[q]=par[np]=nq;
    for(;p&&son[p][ch]==q;p=par[p]) son[p][ch]=nq;
}
inline int gao(){
    int p=rt,sum=0,res=0;
    for(int i=1;i<=m;++i){
        int ch=s[i]-'0';
        if(son[p][ch]) p=son[p][ch],++sum;
        else{
            while(p&&!son[p][ch]) p=par[p];
            if(!p) p=rt,sum=0;
            else sum=mx[p]+1,p=son[p][ch];
        }len[i]=sum;res=max(res,len[i]);
    }return res;
}
inline bool jud(int L){
    int qh=1,qt=0;f[0]=0;
    for(int i=1;i<=m;++i){
        int j=i-L;
        if(j>=0){
            while(qh<=qt&&f[qq[qt]]-qq[qt]<=f[j]-(j)) --qt;
            qq[++qt]=j;
        }while(qh<=qt&&qq[qh]<i-len[i]) ++qh;f[i]=f[i-1];
        if(qh<=qt) f[i]=max(f[i],f[qq[qh]]-qq[qh]+i);
    }return f[m]*10>=m*9;
}
int main(){
//  freopen("a.in","r",stdin);
    int n1=read(),n2=read();last=rt=++n;
    while(n2--){
        scanf("%s",s+1);m=strlen(s+1);last=rt;
        for(int i=1;i<=m;++i) ins(s[i]-'0');
    }while(n1--){
        scanf("%s",s+1);m=strlen(s+1);
        int l=1,r=gao();
        while(l<=r){
            int mid=l+r>>1;
            if(jud(mid)) l=mid+1;
            else r=mid-1;
        }printf("%d\n",l-1);
    }return 0;
}
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页