Codeforces 700E Cool Slogans

Problem

Codeforces

Solution

先建出后缀自动机,然后我们可以按照right集合来dp。不妨设f[x]表示为x节点表示的子串所能得到的最优答案。

那么有状态转移方程 f [ u ] = 1 + max f [ v ] ,当然了,v要满足在u中出现了至少两次。那么可以在parent树上做这样的一个事情,用top表示从节点到根的路径上最优的节点,即最近的一个转移的。为了判断,我们用线段树合并乱搞出一个right集合。当然由于是从parent树上自上而下继承的,那么至少会在pos[u]处出现过一次,那么我们就只需要查询是否出现第二次。则此时的这一个有效区间为 [ p o s [ u ] l e n [ u ] + l e n [ v ] , p o s [ u ] 1 ] ,如果有值则说明出现了第二次。

注意一下pos的转移,hhh我调这个调了40min

Code

#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=400010,maxm=10000010;
int n,tot=1,lst=1,sz,ans=1,p[maxn],a[maxn],l[maxn],pre[maxn],ch[maxn][26];
int pos[maxn],top[maxn],f[maxn],rt[maxn],lc[maxm],rc[maxm];
char s[maxn>>1];
void update(int l,int r,int pos,int &rt)
{
    if(!rt) rt=++sz;
    if(l==r) return ;
    int m=(l+r)>>1;
    if(pos<=m) update(l,m,pos,lc[rt]);
    else update(m+1,r,pos,rc[rt]);
}
int query(int l,int r,int L,int R,int rt)
{
    if(!rt) return 0;
    if(L<=l&&r<=R) return 1;
    int m=(l+r)>>1;
    if(L<=m&&query(l,m,L,R,lc[rt])) return 1;
    if(m<R&&query(m+1,r,L,R,rc[rt])) return 1;
    return 0; 
}
inline int merge(int x,int y)
{
    if(!x||!y) return x|y;
    int nw=++sz;
    lc[nw]=merge(lc[x],lc[y]);
    rc[nw]=merge(rc[x],rc[y]);
    return nw;
}
void insert(int c,int id)
{
    int p=lst,np=++tot;
    lst=np;l[np]=l[p]+1;pos[np]=id;
    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[p]+1==l[q]) pre[np]=q;
        else
        {
            int nq=++tot;l[nq]=l[p]+1;pos[nq]=pos[q];
            memmove(ch[nq],ch[q],sizeof(ch[q]));
            pre[nq]=pre[q];pre[q]=pre[np]=nq;
            for(;ch[p][c]==q;p=pre[p]) ch[p][c]=nq;
        }
    }
    update(1,n,id,rt[lst]);
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    scanf("%d%s",&n,s+1);
    for(int i=1;i<=n;i++) insert(s[i]-'a',i);
    for(int i=1;i<=tot;i++) a[l[i]]++;
    for(int i=1;i<=n;i++) a[i]+=a[i-1];
    for(int i=tot;i;i--) p[a[l[i]]--]=i;
    for(int i=tot;i>1;i--) rt[pre[p[i]]]=merge(rt[pre[p[i]]],rt[p[i]]);
    for(int i=2;i<=tot;i++)
    {
        int x=p[i],fa=pre[x];
        if(fa==1){f[x]=1;top[x]=x;continue;}
        if(query(1,n,pos[x]-l[x]+l[top[fa]],pos[x]-1,rt[top[fa]]))
          f[x]=f[fa]+1,top[x]=x;
        else f[x]=f[fa],top[x]=top[fa];
        if(ans<f[x]) ans=f[x];
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/as_a_kid/article/details/80818496
今日推荐