题意翻译

给出一个长度为$n$的字符串$s[1]$,由小写字母组成。定义一个字符串序列$s[1....k]$,满足性质:$s[i]$在$s[i-1]$ $(i>=2)$中出现至少两次(位置可重叠),问最大的$k$是多少,使得从$s[1]$开始到$s[k]$都满足这样一个性质。

题解

  看了一个中午的代码终于弄懂了……$yyb$大佬强无敌……

  一开始以为是SAM建好之后直接上$dp$,直接用$parent$树上的儿子节点更新父亲,因为parent树上节点不同说明出现次数必定不同。但交上去一发WA了。才发现自己这种想法不能保证$parent$树上的父亲必定在儿子中出现过超过两次……

  还是来说说正解吧。我们先对原串建好SAM,并记录下每一个节点的任意一个$endpos$。考虑一下如何判断一个串是否在另一个串中出现,如果一个串(设串$A$)在另一个串(设串$B$)中出现了大于等于两次,那么在原串的任意位置的串$B$中都出现了两次。

  于是考虑一下维护每一个点的$endpos$集合,这个只要用线段树就行了。如果$A$在$B$中出现了两次,那么$A$的$endpos$集合在$[pos[B]-len[B]+len[A],pos[B]]$中出现了至少两次(其中$pos[B]$表示$B$的任意一个$endpos$)。

  不难发现有一个$dp$,每一个$parent$树上的父亲节点都可能转移到儿子节点,于是从上到下$dp$。又因为$parent$树上父亲是儿子的严格后缀,所以必然在儿子里出现了一次,那么只要考虑$endpos[A]$中是否有元素在$[pos[B]-len[B]+len[A],pos[B]-1]$中就行了

 //minamoto
#include<cstdio>
#include<cstring>
using namespace std;
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,:;}
const int N=;
int fa[N],ch[N][],l[N],f[N],a[N],c[N],pos[N],top[N];char s[N];
int last=,cnt=,n,ans=;
void ins(int c,int k){
int p=last,np=++cnt;last=np,l[np]=l[p]+,pos[np]=k;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=;
else{
int q=ch[p][c];
if(l[q]==l[p]+) fa[np]=q;
else{
int nq=++cnt;l[nq]=l[p]+,pos[nq]=pos[q];
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q],fa[np]=fa[q]=nq;
for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
void calc(){
for(int i=;i<=cnt;++i) ++c[l[i]];
for(int i=;i<=cnt;++i) c[i]+=c[i-];
for(int i=;i<=cnt;++i) a[c[l[i]]--]=i;
}
int L[N*],R[N*],rt[N],tot;
void modify(int &now,int l,int r,int x){
now=++tot;if(l==r) return;
int mid=l+r>>;
if(x<=mid) modify(L[now],l,mid,x);
else modify(R[now],mid+,r,x);
}
int merge(int x,int y){
if(!x||!y) return x|y;
int z=++tot;
L[z]=merge(L[x],L[y]);
R[z]=merge(R[x],R[y]);
return z;
}
int query(int x,int l,int r,int ql,int qr){
if(!x) return ;if(ql<=l&&qr>=r) return ;
int mid=l+r>>;
if(ql<=mid&&query(L[x],l,mid,ql,qr)) return ;
if(qr>mid&&query(R[x],mid+,r,ql,qr)) return ;
return ;
}
int main(){
scanf("%d",&n);
scanf("%s",s+);
for(int i=;i<=n;++i) ins(s[i]-'a',i),modify(rt[last],,n,i);
calc();
for(int i=cnt;i>;--i) rt[fa[a[i]]]=merge(rt[fa[a[i]]],rt[a[i]]);
for(int i=;i<=cnt;++i){
int u=a[i],ff=fa[u];
if(ff==){f[u]=,top[u]=u;continue;}
int x=query(rt[top[ff]],,n,pos[u]-l[u]+l[top[ff]],pos[u]-);
if(x) f[u]=f[ff]+,top[u]=u;
else f[u]=f[ff],top[u]=top[ff];
cmax(ans,f[u]);
}
printf("%d\n",ans);
return ;
}

[CF700E][JZOJ5558]Cool Slogan (后缀自动机+线段树)的更多相关文章

  1. 【CF700E】Cool Slogans 后缀自动机+线段树合并

    [CF700E]Cool Slogans 题意:给你一个字符串S,求一个最长的字符串序列$s_1,s_2,...,s_k$,满足$\forall s_i$是S的子串,且$s_i$在$s_{i-1}$里 ...

  2. BZOJ3413: 匹配(后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...

  3. cf666E. Forensic Examination(广义后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...

  4. 洛谷P2178 [NOI2015]品酒大会(后缀自动机 线段树)

    题意 题目链接 Sol 说一个后缀自动机+线段树的无脑做法 首先建出SAM,然后对parent树进行dp,维护最大次大值,最小次小值 显然一个串能更新答案的区间是\([len_{fa_{x}} + 1 ...

  5. BZOJ1396: 识别子串(后缀自动机 线段树)

    题意 题目链接 Sol 后缀自动机+线段树 还是考虑通过每个前缀的后缀更新答案,首先出现次数只有一次,说明只有\(right\)集合大小为\(1\)的状态能对答案产生影响 设其结束位置为\(t\),代 ...

  6. [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)

    https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...

  7. 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)

    题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...

  8. 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)

    模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...

  9. 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)

    点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...

  10. bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)

    bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...

随机推荐

  1. 排查MySQL事务没有提交导致 锁等待 Lock wait timeout exceeded

    解决思路: select * from information_schema.innodb_trx 之后找到了一个一直没有提交的只读事务, kill 到了对应的线程后ok 了. 转载自:http:// ...

  2. Android开发实战之底部Dialog弹出效果

    在Android开发中,有很多情况下我们需要使用到对话框,遗憾的是,安卓自带的对话框样式不能满足我们实际的需要,所以往往需要我们自定义对话框,具体做法:写一个对话框继承自Dialog实现他的一个构造方 ...

  3. 函数用途:同一域名对应多个IP时,获取指定服务器的远程网页内容

    <?php /************************ * 函数用途:同一域名对应多个IP时,获取指定服务器的远程网页内容 * 创建时间:2008-12-09 * 创建人:张宴(img. ...

  4. 563. Binary Tree Tilt 子节点差的绝对值之和

    [抄题]: Given a binary tree, return the tilt of the whole tree. The tilt of a tree node is defined as ...

  5. eclipse 使用 scons 编译的配置说明

    eclipse版本: eclipse-cpp-kepler-SR1-win32.zip 创建项目必须选择“Makefile Project” 然后进入“Projects  Properities” 先 ...

  6. CloudStack 4.3功能前瞻

    今天CloudStack 4.3已经Feature Freeze了,不会再有新功能加入到这个版本里.我们也可以坐下来看看哪些功能是值得期待的.首先,4.3的UI也秉承扁平化设计,看着更加简洁清爽.见下 ...

  7. 利用SHELL脚本修改当前环境变量

    转自http://www.chinaunix.net/old_jh/7/21485.html 1.背景 ---- 在日常的工作中,为了设置一大批环境变量,我们通常编辑了一个shell程序,包含了多个的 ...

  8. leetcode - 3、Longest Substring Without Repeating Characters

    题目链接:https://leetcode.com/problems/longest-substring-without-repeating-characters/description/ 题目要求: ...

  9. hdu 折线分割平面(递推)

    题解: 首先我们考虑直线的情况: 当n=1时原来的1个平面被分割成了2个: 当n=2时原来的2个平面被分割成了4个: 当n=3时原来的4个平面被分割成了7个: 也就是说F(n)=F(n-1)+n且n= ...

  10. R语言笔记完整版

    [R笔记]R语言函数总结   R语言与数据挖掘:公式:数据:方法 R语言特征 对大小写敏感 通常,数字,字母,. 和 _都是允许的(在一些国家还包括重音字母).不过,一个命名必须以 . 或者字母开头, ...