[CTSC2012] bzoj 2806 Cheat - 广义SAM - 单调队列优化dp - 二分

首先用广义SAM求出每个询问串以每个位置结尾的最长匹配长度,然后二分答案然后dp即可。注意到转移区间显然单调所以单调队列即可。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#define SIG 2
#define N 1200000
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
struct SAM{
    int ch[N<<1][2],fa[N],val[N],node_cnt;
    SAM() { node_cnt=1; }
    inline int new_sam_node(int v,int x=0)
    {   return x=++node_cnt,val[x]=v,x; }
    inline int extend(int las,int w)
    {
        int p=las,np=new_sam_node(val[p]+1);
        while(p&&!ch[p][w]) ch[p][w]=np,p=fa[p];
        if(!p) fa[np]=1;
        else{
            int q=ch[p][w],v=val[p]+1;
            if(val[q]==v) fa[np]=q;
            else{
                int nq=new_sam_node(v);
                fa[nq]=fa[q],fa[q]=fa[np]=nq;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                while(p&&ch[p][w]==q) ch[p][w]=nq,p=fa[p];
            }
        }
        return np;
    }
    inline int run(char *s,int n,int *p)
    {
        for(int i=1,x=1,l=0;i<=n;p[i]=i-l+1,i++)
        {
            int c=s[i]-'0';
            while(x&&!ch[x][c]) l=val[x=fa[x]];
            if(x) x=ch[x][c],l++;else x=1;
        }
        return 0;
    }
}t;
struct Trie{
    queue<int> q;int ch[N][2],las[N],node_cnt;
    Trie() { node_cnt=1; }
    inline int insert(char *s)
    {
        for(int i=1,n=(int)strlen(s+1),x=1,c;i<=n;i++)
        {
            if(!ch[x][c=s[i]-'0'])
                ch[x][c]=++node_cnt;
            x=ch[x][c];
        }
        return 0;
    }
    inline int build_sam(SAM &t)
    {
        while(!q.empty()) q.pop();
        q.push(1),las[1]=1;
        while(!q.empty())
        {
            int x=q.front();q.pop();
            for(int i=0,c;i<SIG;i++)
                if(c=ch[x][i]) las[c]=t.extend(las[x],i),q.push(c);
        }
        return 0;
    }  
}tr;
int dp[N],p[N],q[N],f[N];char s[N];
inline int check(int L,int n)
{
    for(int i=1,fp=1,rp=0,x=0,y=-1;i<=n;f[i]=dp[i]-i,i++)
    {
        dp[i]=dp[i-1];if(p[i]>i-L+1) continue;
        while(y<i-L)
        {
            y++;
            while(fp<=rp&&f[y]>=f[q[rp]]) rp--;
            q[++rp]=y;
        }
        while(x<p[i]-1)
        {
            if(fp<=rp&&q[fp]==x) fp++;
            x++;
        }
        if(fp<=rp) dp[i]=max(dp[i],i+f[q[fp]]);
    }
    return 10*dp[n]>=9*n;
}
int main()
{
    int n,m;scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++) scanf("%s",s+1),tr.insert(s);
    tr.build_sam(t);
    for(int i=1,l,L,R,n,mid;i<=m;i++)
    {
        scanf("%s",s+1),l=(int)strlen(s+1);
        t.run(s,l,p),L=1,R=l,mid=(L+R)>>1;
        while(L<=R)
        {
            if(check(mid,l)) L=mid+1;
            else R=mid-1;mid=(L+R)>>1;
        }
        printf("%d\n",R);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/81360150
今日推荐