天天看点

bzoj 2806 后缀自动机

先把所有作文库连起来建立一个后缀自动机。 对于每一个询问,把字符串拿到自动机上去跑匹配,计算出每一个位置能匹配的最大长度val[i];然后二分一个L值,用dp来检验,设dp[i]为前i个字符的最大匹配数 就有dp[i] = max(dp[j]+i-j | i-val[i] <= j <= i-L)。

维护一个队列存放i-L~i的元素 然后检验队首元素是否满足i-val[i] <= q.front(),假设当前元素不满足 当i变为i+1时 val[i]至多增长1 所以仍不满足条件 以后也都不会满足 所以可以把它弹出队列。

tips:此题遍历字符串数组时用普通的遍历会T,需要用指针来遍历数组

bzoj 2806 后缀自动机
bzoj 2806 后缀自动机
#include <cstdio>
#include <map>
#include <algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define N 2300010
#define ll long long
int root,last,cnt=0,fa[N],mx[N],son[N][3];
ll ans=0;
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;
}
void ins(int ch){
    int p=last,np=++cnt;mx[np]=mx[p]+1,last=np;
    while(p && !son[p][ch]) son[p][ch]=np,p=fa[p];
    if(!p) fa[np]=1;
    else{
        int q=son[p][ch];
        if(mx[q]==mx[p]+1) fa[np]=q;
        else{
            int nq=++cnt;mx[nq]=mx[p]+1;
            memcpy(son[nq],son[q],sizeof(son[q]));
            fa[nq]=fa[q];fa[q]=fa[np]=nq;
            while(son[p][ch]==q) son[p][ch]=nq,p=fa[p];
        }
    }
}
char str[N];
int val[N],dp[N];
bool check(int l,int len)
{
    dp[0]=0;
    queue<int>q;
    for(int i=1;i<=len;i++)
    {
        dp[i]=dp[i-1];
        int p=i-l;
        if(p>=0)
        {
            while(!q.empty()&&dp[p]-p>dp[q.front()]-q.front()) q.pop();
            q.push(p);
        }
        while(!q.empty()&&i-val[i]>q.front()) q.pop();
        if(!q.empty()) dp[i]=max(dp[i],dp[q.front()]+i-q.front());
    }
    return 10*dp[len]>=9*len;
}
void iins(char *s)
{
    for(char *c=s;*c;c++) ins(*c-'0');
    ins(2);
}
int main(){
    int n,m;
    last=root=++cnt;
    n=read();
    m=read();
    while(m--)
    {
        scanf("%s",str);
        iins(str);
        /*for(int j=1;j<=strlen(str+1);j++)
            ins(str[j]-'0');
        ins(2);*/
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%s",str+1);
        int len1=strlen(str+1),p=1,sz=0;
        for(int j=1;j<=len1;j++){
            if(son[p][str[j]-'0']) sz++,p=son[p][str[j]-'0'];
            else{
                while(p&&!son[p][str[j]-'0']) p=fa[p];
                if(!p) sz=0,p=root;
                else sz=mx[p]+1,p=son[p][str[j]-'0'];
            }
            val[j]=sz;
            //printf("%d\n",sz);
        }
        int ans=0,l=0,r=len1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(check(mid,len1)) l=mid+1,ans=mid;
            else r=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}