[cf700E][后缀自动机]Cool Slogans

E. Cool Slogans
time limit per test4 seconds
memory limit per test512 megabytes
inputstandard input
outputstandard output
Bomboslav set up a branding agency and now helps companies to create new logos and advertising slogans. In term of this problems, slogan of the company should be a non-empty substring of its name. For example, if the company name is “hornsandhoofs”, then substrings “sand” and “hor” could be its slogans, while strings “e” and “hornss” can not.

Sometimes the company performs rebranding and changes its slogan. Slogan A is considered to be cooler than slogan B if B appears in A as a substring at least twice (this occurrences are allowed to overlap). For example, slogan A =  “abacaba” is cooler than slogan B =  “ba”, slogan A =  “abcbcbe” is cooler than slogan B =  “bcb”, but slogan A =  “aaaaaa” is not cooler than slogan B =  “aba”.

You are given the company name w and your task is to help Bomboslav determine the length of the longest sequence of slogans s1, s2, …, sk, such that any slogan in the sequence is cooler than the previous one.

Input
The first line of the input contains a single integer n (1 ≤ n ≤ 200 000) — the length of the company name that asks Bomboslav to help. The second line contains the string w of length n, that consists of lowercase English letters.

Output
Print a single integer — the maximum possible length of the sequence of slogans of the company named w, such that any slogan in the sequence (except the first one) is cooler than the previous

Examples
inputCopy
3
abc
output
1
inputCopy
5
ddddd
output
5
inputCopy
11
abracadabra
output
3

sol:

考虑这道题实际上选出一个串,然后在这个串中选出现了2次的子串。
假设选出来的串是s1,s2,s3…sn
我们发现si+1在si中出现的位置如果是x和y,我们总能删掉他x前面和y+len后面的东西使得si+1成为si的border,并且不会使得答案更劣。于是问题转化成了i,j这个子串的价值为他的boder+1。我们考虑因为涉及了子串问题所以我们把他丢到sam上面去做。一个状态中的子串显然是等价的,因为他们的祖先显然是这些子串的后缀,把他们单独拿出来不会使得他的后缀变得更多,并不会使答案增加。我们只要考虑这些祖先是否在这个子串中出现了2次即可。因为出现了两次,我们完全可以把前面的部分砍掉使得他的祖先成为一个boder。那么判断祖先是否在这个子串中出现了2次只要随便从当前状态取出一个right集合的点x,判断祖先的right集合是否出现在[x-val+1,x]2次即可。

#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;

inline int read()
{
    char c;
    bool pd=0;
    while((c=getchar())>'9'||c<'0')
    if(c=='-') pd=1;
    int res=c-'0';
    while((c=getchar())>='0'&&c<='9')
    res=(res<<3)+(res<<1)+c-'0';
    return pd?-res:res;
}
int cnt=1,last=1;
const int N=410000;
int val[N],go[N][26],par[N];
inline void extend(int x)
{
    int p=last;
    int np=last=++cnt;
    val[np]=val[p]+1;
    while(!go[p][x]) go[p][x]=np,p=par[p];
    if(!p) par[np]=1;
    else
    {
        int q=go[p][x];
        if(val[q]==val[p]+1) par[np]=q;
        else
        {
            int nq=++cnt;
            val[nq]=val[p]+1;
            par[nq]=par[q];
            for(int i=0;i<=25;++i) go[nq][i]=go[q][i];
            par[q]=par[np]=nq;
            while(go[p][x]==q) go[p][x]=nq,p=par[p];
        }
    }
}
int tot,fir[N],ed[N*2],nex[N*2];
inline void add(int x,int y)
{
    nex[++tot]=fir[x];fir[x]=tot;ed[tot]=y;
    nex[++tot]=fir[y];fir[y]=tot;ed[tot]=x;
}
int rt[N],key;
const int M=N*20;
int lc[M],rc[M],size[M];
inline int merge(int x,int y)
{
    if(!x) return y;
    if(!y) return x;
    int now=++key;
    size[now]=size[x]+size[y];
    lc[now]=merge(lc[x],lc[y]);
    rc[now]=merge(rc[x],rc[y]);
    return now;
}
inline void modify(int &k,int l,int r,int x)
{
    if(!k) k=++key;
    size[k]++;
    if(l==r) return;
    int mid=l+r>>1;
    if(mid>=x) modify(lc[k],l,mid,x);
    else modify(rc[k],mid+1,r,x);
}
inline void query(int k,int l,int r,int L,int R,int &res)
{
    if(!k) return;
    if(L<=l&&r<=R)
    {
        res+=size[k];
        return;
    }
    int mid=l+r>>1;
    if(mid>=L) query(lc[k],l,mid,L,R,res);
    if(mid< R) query(rc[k],mid+1,r,L,R,res);
}
int R[N],pos[N];
int father[N];
inline void dfs(int u,int fa)
{
    int e,v;
    father[u]=fa;
    if(pos[u])
    {
        modify(rt[u],1,cnt,pos[u]);
        R[u]=pos[u];
    }
    for(e=fir[u];v=ed[e],e;e=nex[e])
    if(v!=fa)
    {
        dfs(v,u);
        rt[u]=merge(rt[u],rt[v]);
        R[u]=max(R[u],R[v]);
    }
}
int ans[N];
int dep[N];
int fans;
int tfa[N];
inline void get_ans(int u,int fa)
{
    int e,v;
    dep[u]=dep[fa]+1;
    for(e=fir[u];v=ed[e],e;e=nex[e])
    if(v!=fa)
    {
        int res=0;
        if(u!=1) query(rt[tfa[u]],1,cnt,R[v]-val[v]+val[father[tfa[u]]]+1,R[v],res);
        if(res>=2||u==1) ans[v]=ans[u]+1,tfa[v]=v;
        else ans[v]=ans[u],tfa[v]=tfa[u];
        if(fans<ans[v]) fans=ans[v];
        get_ans(v,u);
    }
}
char sr[N];
int n;
int main()
{
//  freopen("a.in","r",stdin);
//  freopen("a.out","w",stdout);
    n=read();
    scanf("%s",sr+1);
//  n=strlen(sr+1);
    int anser;
    for(int i=1;i<=n;++i)
    {
        pos[cnt+1]=i;
        if(i==n) anser=cnt+1;
        extend(sr[i]-'a');
    }
    for(int i=1;i<=cnt;++i)
    if(par[i]) add(par[i],i);
    dfs(1,0);
//  dfs();
    get_ans(1,0);
    cout<<fans<<endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值