【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 后缀自动机+线段树合并的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  8. BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并

    题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...

  9. CF 666E Forensic Examination——广义后缀自动机+线段树合并

    题目:http://codeforces.com/contest/666/problem/E 对模式串建广义后缀自动机,询问的时候把询问子串对应到广义后缀自动机的节点上,就处理了“区间”询问. 还要处 ...

随机推荐

  1. SpringBoot整合cxf发布webService

    1. 看看项目结构图 2. cxf的pom依赖 1 <dependency>2 <groupId>org.apache.cxf</groupId>3 <art ...

  2. VirtualBox导入已存在的VHD遇到的uuid冲突问题

    解决方法: 用命令行进入VirtualBox的安装目录,使用下面的命令: (进入命令行窗口,cmd模式) C:\Program Files\Oracle\VirtualBox>VBoxManag ...

  3. v9定时发布的简单实现方法[支持静态生成]

    将以下代码放到 api/count.php 文件最后 的 ?>之前 //add 定时发布审核功能 $modelid = $modelid ? $modelid : intval($_GET['m ...

  4. 将文件导入到SQL server数据库表中的字段中

    一.在要执行的sql server数据库a中执行如下脚本,创建存储过程sp_textcopy /* 将二进制文件导入.导出到数据库相应字段列中 */ CREATE PROCEDURE sp_textc ...

  5. python 源码安装

    1)下载python源码包 http://mirrors.sohu.com/python/3.5.2/Python-3.5.2.tgz 2)安装相关依赖  yum install zlib-devel ...

  6. cordova开发ios炸鸡

    cordova/lib/copy-www-build-step.sh: Permission denied 解决办法: cd platforms/ios/cordova/lib sudo chmod ...

  7. Dubbo -- 系统学习 笔记 -- 配置参考手册

    Dubbo -- 系统学习 笔记 -- 目录 配置参考手册 <dubbo:service/> <dubbo:reference/> <dubbo:protocol/> ...

  8. Python easyGUI 文件对比 覆盖保存

    #在35-3的基础上进行优化,当用户点击ok按钮的时候,对打开的文件进行检查是否修改.# 如果修改过,则提示覆盖保存.放弃保存.另存为并实现相应的功能 1 import easygui as g im ...

  9. 【代码审计】TuziCMS_v3.0_任意文件删除漏洞分析

      0x00 环境准备 TuziCMS官网:http://www.tuzicms.com/ 网站源码版本:TuziCMS_v3.0_20161220 程序源码下载:http://www.tuzicms ...

  10. 打破基于OpenResty的WEB安全防护(CVE-2018-9230)

    原文首发于安全客,原文链接:https://www.anquanke.com/post/id/103771 0x00 前言 ​ OpenResty® 是一个基于 Nginx 与 Lua 的高性能 We ...