BZOJ 2806 CTSC2012 熟悉的文章 cheat

版权声明:写得不好,转载请通知一声,还请注明出处,感激不尽 https://blog.csdn.net/As_A_Kid/article/details/82018404

Problem

BZOJ

Solution

注意到答案具有单调性,不妨考虑二分答案。设f[i]表示询问串前i位能匹配的最大长度,那么我们容易写出如下dp方程,其中pi表示的是当前串能匹配的最大长度:

f i = max j [ i p i , i L ] ( f i 1 , f j + i j )

那么可以先对主串建出广义后缀自动机,对于询问的串S,我们可以先跑出p数组,这样单次询问的复杂度是 O ( l e n 2 log l e n )

把后面的那个式子丢进单调队列里维护就可以做到 O ( n ) 的dp了。

时间复杂度 O ( l e n log l e n )

Code

#include <cstring>
#include <cstdio>
#define rg register
using namespace std;
const int maxn=2000010;
int n,m,lst,sz=1,len,ans,head;
int lth[maxn],f[maxn],pre[maxn],ch[maxn][2],l[maxn],q[maxn];
char s[maxn];
template <typename Tp> inline void getmax(Tp &x,Tp y){if(y>x) x=y;}
void insert(int c)
{
    int p=lst,np=++sz;
    lst=np;l[np]=l[p]+1;
    for(;p&&!ch[p][c];p=pre[p]) ch[p][c]=np;
    if(!p) pre[np]=1;
    else
    {
        int q=ch[p][c];
        if(l[q]==l[p]+1) pre[np]=q;
        else
        {
            int nq=++sz;l[nq]=l[p]+1;
            ch[nq][0]=ch[q][0];ch[nq][1]=ch[q][1];
            pre[nq]=pre[q];pre[q]=pre[np]=nq;
            for(;ch[p][c]==q;p=pre[p]) ch[p][c]=nq;
        }
    }
}
void match()
{
    int x=1,now=0;
    for(rg int i=1;i<=len;i++)
    {
        if(ch[x][s[i]-'0']) now++,x=ch[x][s[i]-'0'];
        else
        {
            while(x&&!ch[x][s[i]-'0']) x=pre[x];
            if(x) now=l[x]+1,x=ch[x][s[i]-'0'];
            else x=1,now=0;
        }
        lth[i]=now;
    }
}
int check(int k)
{
    int head=0,tail=-1;
    for(rg int i=0;i<k;i++) f[i]=0;
    for(rg int i=k;i<=len;i++)
    {
        f[i]=f[i-1];
        while(head<=tail&&f[i-k]-i+k>f[q[tail]]-q[tail]) tail--;
        q[++tail]=i-k;
        while(head<=tail&&q[head]<i-lth[i]) head++;
        if(head<=tail) getmax(f[i],f[q[head]]-q[head]+i);
    }
    return f[len]*10>=len*9;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    scanf("%d%d",&n,&m);
    while(m--)
    {
        scanf("%s",s+1);lst=1;
        for(rg int i=1;s[i];i++) insert(s[i]-'0');
    }
    while(n--)
    {
        int L=1,R,mid;
        scanf("%s",s+1);
        R=len=strlen(s+1);
        match();
        while(L<=R)
        {
            mid=(L+R)>>1;
            if(check(mid)) ans=mid,L=mid+1;
            else R=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/As_A_Kid/article/details/82018404