CF700E Cool Slogans

题目

\(yyb\)代码真是开心的一批

这道题看上去确实是没有什么思路

之后想一想大概是在\(parent\)树上从儿子向父亲转移

但是好像不太对的样子

于是考虑在\(parent\)树上从父亲向儿子转移

显然父亲一定是儿子的一个后缀,于是父亲肯定在儿子里出现了一次

至于如何保证其出现第二次呢,我们可以用线段树合并来维护

用线段树合并来维护一下\(endpos\)集合,看一下父亲有没有在儿子里出现过两次就好了

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define re register
#define maxn 400005
#define M 8000005
#define LL long long
#define inf 999999999
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
    char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
char S[maxn];
int fa[maxn],len[maxn],son[maxn][26],rt[maxn],top[maxn],pos[maxn],dp[maxn];
int tax[maxn],A[maxn]; 
int l[M],r[M],d[M];
int n,tot,lst=1,cnt=1,ans=1;
int change(int now,int x,int y,int pos)
{
    if(!now) now=++tot;d[now]++;
    if(x==y) return now;
    int mid=x+y>>1;
    if(pos<=mid) l[now]=change(l[now],x,mid,pos);
        else r[now]=change(r[now],mid+1,y,pos);
    return now; 
}
int merge(int a,int b,int x,int y)
{
    if(!a) return b;if(!b) return a;
    int root=++tot;
    if(x==y) {d[root]=d[a]+d[b];return root;}
    int mid=x+y>>1; 
    l[root]=merge(l[a],l[b],x,mid);r[root]=merge(r[a],r[b],mid+1,y);
    d[root]=d[l[root]]+d[r[root]];
    return root;
}
inline void ins(int c,int o)
{
    int p=++cnt,f=lst; lst=p;
    len[p]=len[f]+1,rt[p]=change(rt[p],1,n,o),pos[p]=o;
    while(f&&!son[f][c]) son[f][c]=p,f=fa[f];
    if(!f) {fa[p]=1;return;}
    int x=son[f][c];
    if(len[f]+1==len[x]) {fa[p]=x;return;}
    int y=++cnt;
    len[y]=len[f]+1,fa[y]=fa[x],fa[x]=fa[p]=y;
    for(re int i=0;i<26;i++) son[y][i]=son[x][i];
    while(f&&son[f][c]==x) son[f][c]=y,f=fa[f];
}
int query(int now,int x,int y,int lx,int ry)
{
    if(!now) return 0;
    if(lx<=x&&ry>=y) return d[now]>0;
    int mid=x+y>>1;
    if(ry<=mid) return query(l[now],x,mid,lx,ry);
    if(lx>mid) return query(r[now],mid+1,y,lx,ry);
    return query(l[now],x,mid,lx,ry)|query(r[now],mid+1,y,lx,ry);
}
int main()
{
    scanf("%d",&n);scanf("%s",S+1);
    for(re int i=1;i<=n;i++) ins(S[i]-'a',i);
    for(re int i=1;i<=cnt;i++) tax[len[i]]++;
    for(re int i=1;i<=n;i++) tax[i]+=tax[i-1];
    for(re int i=1;i<=cnt;i++) A[tax[len[i]]--]=i;
    for(re int i=cnt;i>1;--i) 
        rt[fa[A[i]]]=merge(rt[fa[A[i]]],rt[A[i]],1,n),pos[fa[A[i]]]=max(pos[fa[A[i]]],pos[A[i]]);
    for(re int i=2;i<=cnt;i++)
    {
        int x=A[i];
        if(fa[x]==1) {dp[x]=1;top[x]=x;continue;}
        int now=query(rt[top[fa[x]]],1,n,pos[x]-len[x]+len[top[fa[x]]],pos[x]-1);
        if(now) dp[x]=dp[fa[x]]+1,top[x]=x;
            else dp[x]=dp[fa[x]],top[x]=top[fa[x]];
        ans=max(ans,dp[x]);
    } 
    printf("%d\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/asuldb/p/10333849.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值