Description

给定一个串 \(S\),求一个序列 \(a_i\),满足 \(a_i\) 是原串的子串,且 \(a_i\) 在 \(a_{i-1}\) 中至少出现两次,求这个序列的最大的长度

题面

Solution

根据后缀自动机的性质可以做,首先建出 \(parent\) 树,然后满足条件的序列一定是树上的一条链去掉一些节点

然后就是需要满足出现两次的要求了:

因为是祖先关系,所以至少出现了一次,设 \(pos[i]\) 表示节点 \(i\) 所接受的子串都是以原串中 \(pos[i]\) 这个位置结尾的

另外一次我们就找到这个子串在原串中所代表的区间:\([pos[x]-(len[x]-len[fa[x]]),pos[x]]\),如果这个节点在某个儿子节点所代表的区间中出现了两次,那么就符合要求了

我们用线段树维护 \(pos\) 集合就行了,我们从下往上线段树合并就行了

#include<bits/stdc++.h>
using namespace std;
const int N=4e5+10;
int n,ch[N][26],fa[N],cur=1,cnt=1,len[N],b[N],f[N],ans=0;char s[N];
int pos[N],rt[N],ls[N*30],rs[N*30],tt=0,head[N],nxt[N],to[N],num=0;
inline void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;}
inline void Modify(int &x,int l,int r,int sa){
if(!x)x=++tt;
if(l==r)return ;
int mid=(l+r)>>1;
if(sa<=mid)Modify(ls[x],l,mid,sa);
else Modify(rs[x],mid+1,r,sa);
}
inline void ins(int c){
int p=cur;cur=++cnt;len[cur]=len[p]+1;
for(;p && !ch[p][c];p=fa[p])ch[p][c]=cur;
if(!p)fa[cur]=1;
else{
int q=ch[p][c];
if(len[p]+1==len[q])fa[cur]=q;
else{
int nt=++cnt;len[nt]=len[p]+1;
memcpy(ch[nt],ch[q],sizeof(ch[nt]));
fa[nt]=fa[q];fa[q]=fa[cur]=nt;
for(;p && ch[p][c]==q;p=fa[p])ch[p][c]=nt;
}
}
}
inline int merge(int x,int y){
if(!x || !y)return x+y;
int c=++tt;
ls[c]=merge(ls[x],ls[y]);
rs[c]=merge(rs[x],rs[y]);
return c;
}
inline void dfs(int x){
for(int i=head[x];i;i=nxt[i]){
dfs(to[i]),rt[x]=merge(rt[x],rt[to[i]]);
if(!pos[x])pos[x]=pos[to[i]];
}
}
inline bool qry(int x,int l,int r,int sa,int se){
if(!x)return false;
if(sa<=l && r<=se)return true;
int mid=(l+r)>>1;
if(se<=mid)return qry(ls[x],l,mid,sa,se);
if(sa>mid)return qry(rs[x],mid+1,r,sa,se);
return qry(ls[x],l,mid,sa,mid)|qry(rs[x],mid+1,r,mid+1,se);
}
inline void dfs1(int x){
for(int i=head[x],t,u;i;i=nxt[i]){
u=to[i];
t=qry(rt[b[x]],1,n,pos[u]-len[u]+len[b[x]],pos[u]-1);
if(t)f[u]=f[x]+1,b[u]=u;
else f[u]=f[x],b[u]=b[x];
dfs1(u);
}
}
int main(){
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
scanf("%d%s",&n,s+1);
for(int i=1;i<=n;i++)
ins(s[i]-'a'),pos[cur]=i,Modify(rt[cur],1,n,i);
for(int i=2;i<=cnt;i++)link(fa[i],i);
dfs(1);
for(int i=head[1];i;i=nxt[i])b[to[i]]=to[i],f[to[i]]=1,dfs1(to[i]);
for(int i=2;i<=cnt;i++)ans=max(ans,f[i]);
printf("%d\n",ans);
return 0;
}

Codeforces 700E. Cool Slogans的更多相关文章

  1. Codeforces 700E. Cool Slogans 字符串,SAM,线段树合并,动态规划

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF700E.html 题解 首先建个SAM. 一个结论:对于parent树上任意一个点x,以及它所代表的子树内任 ...

  2. Codeforces.700E.Cool Slogans(后缀自动机 线段树合并 DP)

    题目链接 \(Description\) 给定一个字符串\(s[1]\).一个字符串序列\(s[\ ]\)满足\(s[i]\)至少在\(s[i-1]\)中出现过两次(\(i\geq 2\)).求最大的 ...

  3. [LOJ 6288]猫咪[CF 700E]Cool Slogans

    [LOJ 6288]猫咪[CF 700E]Cool Slogans 题意 给定一个字符串 \(T\), 求一个最大的 \(K\) 使得存在 \(S_1,S_2,\dots,S_k\) 满足 \(S_1 ...

  4. SA / SAM 题目集

    上一次做 SA / SAM 相关的题还要数到某场毒瘤 NOIP 模拟赛--这么久没做了都快忘光了--写点东西记录一些最近做到的水好题. LOJ2059 「TJOI / HEOI2016」字符串 题意 ...

  5. 【CF700E】Cool Slogans(后缀自动机)

    [CF700E]Cool Slogans(后缀自动机) 题面 洛谷 CodeForces 题解 构建后缀自动机,求出后缀树 现在有个比较明显的\(dp\) 设\(f[i]\)表示从上而下到达当前点能够 ...

  6. Codeforces 1063F - String Journey(后缀数组+线段树+dp)

    Codeforces 题面传送门 & 洛谷题面传送门 神仙题,做了我整整 2.5h,写篇题解纪念下逝去的中午 后排膜拜 1 年前就独立切掉此题的 ymx,我在 2021 年的第 5270 个小 ...

  7. python爬虫学习(5) —— 扒一下codeforces题面

    上一次我们拿学校的URP做了个小小的demo.... 其实我们还可以把每个学生的证件照爬下来做成一个证件照校花校草评比 另外也可以写一个物理实验自动选课... 但是出于多种原因,,还是绕开这些敏感话题 ...

  8. 【Codeforces 738D】Sea Battle(贪心)

    http://codeforces.com/contest/738/problem/D Galya is playing one-dimensional Sea Battle on a 1 × n g ...

  9. 【Codeforces 738C】Road to Cinema

    http://codeforces.com/contest/738/problem/C Vasya is currently at a car rental service, and he wants ...

随机推荐

  1. Alpha第七天

    Alpha第七天 听说 031502543 周龙荣(队长) 031502615 李家鹏 031502632 伍晨薇 031502637 张柽 031502639 郑秦 1.前言 任务分配是VV.ZQ. ...

  2. 201621123040《Java程序设计》第13周学习总结

    1.本周学习总结 2.为你的系统增加网络功能(购物车.图书馆管理.斗地主等)-分组完成 2.1简述你想为你的系统增加什么网络功能?设计思路是什么? 创建服务器端端口(3333),当用户以客户端身份访问 ...

  3. 开启Linux的share

    1.挂载Install  Vmare Tool 2.解压VMwareTools.tar.gz 3.安装share目录 4.shared Folders Enabled 添加共享的目录. 在自己的Unb ...

  4. C# if判断语句执行顺序

    DataTable dt = null; )//不报错,因为先执行dt != null 成立时才执行dt.Rows.Count > 0 { } && dt != null)//报 ...

  5. 用‘+=’拼接字符串,打印时总会出现一个undefined

    var str; for(var i = 0; i < 5; i++){ str += String(i); } console.log(str); 他喵的,打印的结果竟然是"unde ...

  6. Linux入门:vi 和 vim

    vi编辑器是所有Unix及Linux系统下标准的编辑器,它的强大不逊色于任何最新的文本编辑器. 本文介绍了vi (vim)的基本使用方法,但对于普通用户来说基本上够了! 转自:http://www.l ...

  7. SpringCloud的DataRest(四)restful特性展示

    一.get - list - http://10.110.20.16:8391/BusiSys/company?page=0&size=5&sort=comp_id,asc 二.pos ...

  8. Spring知识点回顾(02)AOP

    一.注解拦截 二.方法规则拦截

  9. 敏捷项目需求拆解&发现用户故事

    需求文档和敏捷中的Epic,User Story, Task之间是什么关系以及如何将需求文档转换成敏捷方式的描述,指导开发人员. 一直是很多公司团队比较困扰的问题,那么最近笔者为了解决这些问题,上了一 ...

  10. 一个适用于单页应用,返回原始滚动条位置的demo

    如题,最近做一个项目时,由于页面太长,跳转后在返回又回到初始位置,不利于用户体验,需要每次返回到用户离开该页面是的位置.由于是移动端项目,使用了移动端的套ui框架framework7,本身框架的机制是 ...