【CF700E】Cool Slogans 后缀自动机+线段树合并
【CF700E】Cool Slogans
题意:给你一个字符串S,求一个最长的字符串序列$s_1,s_2,...,s_k$,满足$\forall s_i$是S的子串,且$s_i$在$s_{i-1}$里出现了2次。
$|S|\le 10^5$
题解:容易想到pre树的性质。定义一个字符串的tail为它的出现次数>=2的最长的后缀。对于结束节点来说,它的tail就是它的pre。但是对于一般的点,我们需要不断沿着pre向上找,找到第一个在原串出现2次的节点才能得到tail。具体做法是,我们可以记录spre[i]表示最上面一个没有在i中出现两次的点,f[i]代表从沿着spre走到根的路径长度(spre[i]也是最上面一个f不变的点)。那么,如果i包含sp[pre[i]],则sp[i]=i,f[i]=f[pre[i]]+1;否则sp[i]=sp[pre[i]],f[i]=f[pre[i]]。
那么如何检查串a是否在串b中出现2次呢?可以用线段树维护right集合,如果串a在指定范围内出现过,则说明a在b中出现了2次。如何维护right集合呢?线段树合并即可。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std; const int maxn=400010;
int n,tot,Cnt,cnt,last,ans;
int ch[maxn][26],pre[maxn],mx[maxn],to[maxn],nxt[maxn],head[maxn],f[maxn],pos[maxn],rt[maxn],val[maxn],sf[maxn];
struct sag
{
int ls,rs;
}s[maxn*50];
char str[maxn];
inline void extend(int x)
{
int np=++Cnt,p=last;
mx[np]=mx[p]+1,last=np;
for(;p&&!ch[p][x];p=pre[p]) ch[p][x]=np;
if(!p) pre[np]=1;
else
{
int q=ch[p][x];
if(mx[q]==mx[p]+1) pre[np]=q;
else
{
int nq=++Cnt;
mx[nq]=mx[p]+1,pre[nq]=pre[q],pre[np]=pre[q]=nq;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
for(;p&&ch[p][x]==q;p=pre[p]) ch[p][x]=nq;
}
}
}
inline void add(int a,int b)
{
to[cnt]=b,nxt[cnt]=head[a],head[a]=cnt++;
}
void insert(int l,int r,int &x,int a)
{
x=++tot;
if(l==r) return ;
int mid=(l+r)>>1;
if(a<=mid) insert(l,mid,s[x].ls,a);
else insert(mid+1,r,s[x].rs,a);
}
int merge(int a,int b)
{
if(!a||!b) return a^b;
int c=++tot;
s[c].ls=merge(s[a].ls,s[b].ls),s[c].rs=merge(s[a].rs,s[b].rs);
return c;
}
bool count(int l,int r,int x,int a,int b)
{
if(!x) return 0;
if(a<=l&&r<=b) return 1;
int mid=(l+r)>>1;
if(b<=mid) return count(l,mid,s[x].ls,a,b);
if(a>mid) return count(mid+1,r,s[x].rs,a,b);
return count(l,mid,s[x].ls,a,b)|count(mid+1,r,s[x].rs,a,b);
}
void dfs1(int x)
{
int i;
for(i=head[x];i!=-1;i=nxt[i]) dfs1(to[i]),pos[x]=pos[to[i]],rt[x]=merge(rt[x],rt[to[i]]);
}
void dfs2(int x)
{
ans=max(ans,f[x]);
int i;
for(i=head[x];i!=-1;i=nxt[i])
{
int ct=count(0,n-1,rt[sf[x]],pos[to[i]]+mx[sf[x]]-mx[to[i]],pos[to[i]]-1);
if(ct) f[to[i]]=f[x]+1,sf[to[i]]=to[i];
else f[to[i]]=f[x],sf[to[i]]=sf[x];
dfs2(to[i]);
}
}
int main()
{
//freopen("cf700E.in","r",stdin);
scanf("%d%s",&n,str);
int i;
Cnt=last=1;
memset(head,-1,sizeof(head));
for(i=0;i<n;i++) extend(str[i]-'a'),pos[last]=i,insert(0,n-1,rt[last],i);
for(i=2;i<=Cnt;i++) add(pre[i],i);
dfs1(1);
for(i=head[1];i!=-1;i=nxt[i]) f[to[i]]=1,sf[to[i]]=to[i],dfs2(to[i]);
printf("%d",ans);
return 0;
}
【CF700E】Cool Slogans 后缀自动机+线段树合并的更多相关文章
- Codeforces.700E.Cool Slogans(后缀自动机 线段树合并 DP)
题目链接 \(Description\) 给定一个字符串\(s[1]\).一个字符串序列\(s[\ ]\)满足\(s[i]\)至少在\(s[i-1]\)中出现过两次(\(i\geq 2\)).求最大的 ...
- BZOJ3413: 匹配(后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...
- cf666E. Forensic Examination(广义后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...
- [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)
https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...
- 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)
模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...
- 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)
点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...
- bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)
bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...
- BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并
题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...
- CF 666E Forensic Examination——广义后缀自动机+线段树合并
题目:http://codeforces.com/contest/666/problem/E 对模式串建广义后缀自动机,询问的时候把询问子串对应到广义后缀自动机的节点上,就处理了“区间”询问. 还要处 ...
随机推荐
- geoserver 数据图层输出格式
1.WMS服务请求参数 一般WMS的请求地址如下: http://localhost:8080/geoserver/topp/wms?service=WMS&versi on=1.1.0&am ...
- JS去除字符串左右两端的空格
去除字符串左右两端的空格,在vbscript里面可以轻松地使用 trim.ltrim 或 rtrim,但在js中却没有这3个内置方法,需要手工编写.下面的实现方法是用到了正则表达式,效率不错,并把这三 ...
- [原]unity3d ios平台内存优化(一)
关于内存优化,人云亦云 各有己见.本文将通过设置Strpping Level ,减少内存使用. 先看三幅图: 1.没做任何优化,默认选项 2.设置Stripping level 为 Use micro ...
- [Model] LeNet-5 by Keras
典型的卷积神经网络. 数据的预处理 Keras傻瓜式读取数据:自动下载,自动解压,自动加载. # X_train: array([[[[ 0., 0., 0., ..., 0., 0., 0.], [ ...
- ab压测札记(Apache Bench)
1 ab安装 ab实际上是apache httpd里面的一个工具或者说子模块,安装apache httpd可以参考另一篇文章JBOSS集群的2.3节 安装目录:/apache目录/bin/,如下 2 ...
- ios开发之--调试方法
概述 基本操作 全局断点 条件断点 开启僵尸对象 LLDB命令 概述 在开发项目的工程中,肯定会遇到各种各样的bug,且大多数的bug都和自己有关:那么在和bug斗智斗勇的过程中,如果能快速准确的一击 ...
- ios开发之 -- stringByAddingPercentEscapesUsingEncoding方法被替换 iOS9.0
最近在项目中,发现之前的一个方法已经不被建议使用了. 该方法名即题目中提到的: stringByAddingPercentEscapesUsingEncoding,这个方法是用来进行转码的,即将汉字转 ...
- Tomcat漏洞利用与安全加固实例分析
Tomcat中间件经常遇到的漏洞: 1.Tomcat默认存在一个管理后台,默认的管理地址是http://IP或域名:端口号/manager/html 2.Axis2默认口令安全漏洞,默认的管理地址是h ...
- 如何在LSI MegaRAID BIOS里设定RAID 10与Hot Spare
1. 同时按下 ”Ctrl + H” 进入MegaRAID WebBIOS 画面,可以看到所有物理硬盘 (Physical Drives) 的信息.请在左边视窗点选“Configuration Wiz ...
- iOS开发-UIImageView的contentMode属性
UIImageView 的contentMode这个属性是用来设置图片的显示方式,如居中.居右,是否缩放等,有以下几个常量可供设定:UIViewContentModeScaleToFillUIView ...