Luogu-4022 [CTSC2012]熟悉的文章

广义后缀自动机+DP

对于作文库建出广义后缀自动机,广义自动机就是在每次添加一个字符串之前把(last=0),然后正常添加就好了

对于每个询问串,预处理出每个位置(i)能向前匹配的最长长度(pp[i])

二分长度(L),对于位置(i),设往前匹配到(j),满足(i-pp[i]<=j<=i-L), 则中间新增的匹配长度为(i-j),前面的匹配长为(f[j]),则(f[i]=f[j]+i-j)

因为(i)是每次(+1)的,(pp[i])每次有可能(+1),所以(i-pp[i])是单调不降的,可以用单调队列来维护(f[i]-i)的值

#include<map>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=2e6+100;
struct SAM{
    int son[maxn][2],len[maxn],fa[maxn],pp[maxn];
    int tot,last,m,st[maxn],l,r,f[maxn];
    SAM(){tot=last=0,fa[0]=-1;}
    void insert(int x){
        int p=last,np=++tot;
        len[np]=len[p]+1;
        while(~p&&!son[p][x])
            son[p][x]=np,p=fa[p];
        if(p==-1)
            fa[np]=0;
        else{
            int q=son[p][x];
            if(len[q]==len[p]+1)
                fa[np]=q;
            else{
                int nq=++tot;
                memcpy(son[nq],son[q],sizeof(son[q]));
                fa[nq]=fa[q];
                len[nq]=len[p]+1;
                fa[q]=fa[np]=nq;
                while(~p&&son[p][x]==q)
                    son[p][x]=nq,p=fa[p];
            }
        } 
        last=np;
    }
    void getlen(char *s,int n){
        int x=0,cnt=0;
        for(int i=1;i<=n;i++){
            int v=s[i]-'0';
            while(~x&&son[x][v]==0)
                x=fa[x];
            if(x==-1){
                x=cnt=0;
                pp[i]=0;
                continue;
            }
            cnt=min(cnt,len[x])+1;
            x=son[x][v];
            pp[i]=cnt;
        }
    }
    bool check(char *s,int n,int L){
        st[0]=0,r=0,l=1,f[0]=0;
        for(int i=1;i<=n;i++){
            f[i]=f[i-1];
            if(i-L<0) continue;
            while(r>=l&&f[st[r]]-st[r]<=f[i-L]-i+L) r--;
            st[++r]=i-L;
            while(r>=l&&st[l]<i-pp[i]) l++;
            if(r>=l)
            f[i]=max(f[i],f[st[l]]+i-st[l]);
        }
        return 10*f[n]>=9*n;
    }
}sam;
int n,m;
char s[maxn];
int main(){
//	freopen("4022.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%s",s+1);
        int len=strlen(s+1);
        sam.m=max(sam.m,len);
        sam.last=0;
        for(int i=1;i<=len;i++) sam.insert(s[i]-'0');
    }
    while(n--){
        scanf("%s",s+1);
        int len=strlen(s+1);
        sam.getlen(s,len);
        int l=1,r=len+1,mid,ans;
        while(l<r){
            mid=l+r>>1;
            if(sam.check(s,len,mid))
                l=mid+1,ans=mid;
            else
                r=mid;
        }
        printf("%d
",ans);
    }
    return 0;
}







原文地址:https://www.cnblogs.com/nianheng/p/10066428.html