codeforces 700E 后缀自动机+线段树合并

每次只算s[i-1]s[i]的后缀的情况,显然是不会影响答案的。因为如果s[i-1]不是s[i]的后缀,那么我们把不与s[i-1]匹配的那后面一截都去掉,s[i]就会变短。如果没变短之前它在某一个字符串里出现过了,那么变短后显然还是出现过的。

于是想到后缀自动机,建立后缀自动机。既然有“出现两次”这个条件,那么显然与每个节点的right集合(或者说end-pos集合?)有关。

想到一个dp:在parent树上,以节点x所代表的子串作为s[k],k最大是多少。这个只需要判断x的父亲(其实准确的说不是父亲,下面会讲)所代表的子串是否在x中出现了两次。那么令step[x]表示x代表的最长子串长度,假设x的right集合中存在一个值 p o s ,那么 p o s 显然已经在x父亲的right集合中了,我们只要判断x父亲的right集合中有没有一个元素,在区间 [ p o s s t e p ( x ) + s t e p ( f a ) , p o s 1 ] 中即可判断父亲是否出现两次。

可以发现,我们以上的做法都只考虑父亲代表的最长串都必须出现在x代表的最长串中,这样为什么是没有问题的呢?

如图,假如父亲代表的一个较短的串(粉色)被包含在x代表的最长串(蓝色)中,但是父亲代表的较长串(红色)没有被包含进蓝色中。
这就说明x不能代表绿色的串,进而说明在绿色串没有完整出现的情况下,蓝色串还完整的出现过一次。进而说明粉色串在红色串没有完整的出现的情况下出现过一次,进而说明粉色和红色的串不能被同一个节点代表。
所以只考虑最长串之间的关系没有问题。
抽象画师litble

然后,其实我们考虑的,也不能说是父亲,因为在实际计算时为了方便,我们的f[x]应该为以x及x的父亲代表的串为s[k]的最大k。此时如果x的父亲没有在x中出现两次,那么f[x]=f[fa[x]],而显然x的父亲代表的串更短,更容易在以x为父亲的节点y代表的串中出现。所以我们记录一下要查询哪个点代表的子串在当前点x代表的子串中出现即可(代码中的top)。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
const int N=400005;
int n,SZ,last,QAQ,ans;
char s[N];int ch[N][26],pre[N],step[N],pos[N];
void ins(int x,int id) {
    int np=++SZ,p=last;
    step[np]=step[p]+1,pos[np]=id,last=np;
    while(!ch[p][x]&&p) ch[p][x]=np,p=pre[p];
    if(!p) pre[np]=1;
    else {
        int q=ch[p][x];
        if(step[q]==step[p]+1) pre[np]=q;
        else {
            int nq=++SZ; step[nq]=step[p]+1;
            pos[nq]=pos[q],pre[nq]=pre[q];
            for(RI i=0;i<26;++i) ch[nq][i]=ch[q][i];
            pre[q]=pre[np]=nq;
            while(ch[p][x]==q&&p) ch[p][x]=nq,p=pre[p];
        }
    }
}

struct node{int ls,rs;}tr[N*25];
int rt[N],T[N],p[N],f[N],top[N];
void chan(int &x,int s,int t,int wz) {
    if(!x) x=++QAQ;
    if(s==t) return;
    int mid=(s+t)>>1;
    if(wz<=mid) chan(tr[x].ls,s,mid,wz);
    else chan(tr[x].rs,mid+1,t,wz);
}
int merge(int x,int y) {
    if(!x||!y) return x|y;
    int k=++QAQ;
    tr[k].ls=merge(tr[x].ls,tr[y].ls);
    tr[k].rs=merge(tr[x].rs,tr[y].rs);
    return k;
}
int query(int x,int l,int r,int s,int t) {
    if(!x) return 0;
    if(l<=s&&t<=r) return 1;
    int mid=(s+t)>>1;
    if(l<=mid&&query(tr[x].ls,l,r,s,mid)) return 1;
    if(mid+1<=r&&query(tr[x].rs,l,r,mid+1,t)) return 1;
    return 0;
}
int main()
{
    scanf("%d",&n);scanf("%s",s+1);
    SZ=last=1,ans=1;
    for(RI i=1;i<=n;++i) ins(s[i]-'a',i),chan(rt[last],1,n,i);
    for(RI i=1;i<=SZ;++i) ++T[step[i]];
    for(RI i=1;i<=SZ;++i) T[i]+=T[i-1];
    for(RI i=SZ;i>=1;--i) p[T[step[i]]--]=i;
    for(RI i=SZ;i>1;--i) rt[pre[p[i]]]=merge(rt[pre[p[i]]],rt[p[i]]);
    for(RI i=2;i<=SZ;++i) {
        int x=p[i],fa=pre[x];
        if(fa==1) {f[x]=1,top[x]=x;continue;}
        int kl=query(rt[top[fa]],pos[x]-step[x]+step[top[fa]],pos[x]-1,1,n);
        if(kl) f[x]=f[fa]+1,top[x]=x;
        else f[x]=f[fa],top[x]=top[fa];
        if(f[x]>ans) ans=f[x];
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/litble/article/details/81179442