CF700E-Cool Slogans【SAM,线段树合并,dp】
正题
题目链接:https://www.luogu.com.cn/problem/CF700E
题目大意
给出一个字符串\(S\),求一个最大的\(k\)使得存在\(k\)个字符串其中\(s_1\)是\(S\)的子串,\(s_{i+1}\)在\(s_i\)中出现了至少\(2\)次。
解题思路
首先我们需要有两个结论
- \(s_{i+1}\)一定是\(s_i\)的其中一个后缀。因为如果\(s_{i+1}\)不是\(s_i\)的一个后缀,那么\(s_i\)去掉后面那一部分不会影响匹配数并且更短,也就是更优
- 对于\(parents\)树上的一对父子\(x,y\),\(y\)代表的所以字符串与\(x\)最长串的匹配数均相等。因为如果有不等的,那么证明\(y\)中的字符串的出现\(endpos\)集合不同,不符合\(\text{SAM}\)的定义,故不成立。
这样我们就可以在\(\text{SAM}\)上进行\(dp\)了,因为第一个结论我们可以直接在\(fail\)树上\(dp\),然后第二个结论让我们能够使用每个节点最长的串来进行匹配,因为这不会影响答案。
现在我们需要考虑如何判断一个节点的串是否在另一个它的祖先节点的串中出现了两次,首先作为后缀已经出现了一次,然后只需要判断是否包含一个出现在\([pos_x-len_x+len_y,pos_x-1]\)的串就好了,因为\(endpos\)在这个范围内出现的串一定是与字符串\(x\)相同的。
用线段树合并维护每个节点包含的串的位置就好了,还有就是要从上往下转移。
时间复杂度\(O(n\log n)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=4e5+10,M=N<<6;
int n,cnt,last,ans,c[N],p[N],f[N],top[N];
int rt[N],pos[N],len[N],fa[N],ch[N][26];
char s[N];
struct Seq_Tree{
int w[M],ls[M],rs[M],cnt;
int Change(int x,int L,int R,int pos){
int y=++cnt;w[y]=w[x]+1;
if(L==R)return y;
int mid=(L+R)>>1;
if(pos<=mid)ls[y]=Change(ls[x],L,mid,pos),rs[y]=rs[x];
else ls[y]=ls[x],rs[y]=Change(rs[x],mid+1,R,pos);
return y;
}
int Merge(int x,int y,int L,int R){
if(!x||!y)return x|y;
int p=++cnt;w[p]=w[x]+w[y];
if(L==R)return p;
int mid=(L+R)>>1;
ls[p]=Merge(ls[x],ls[y],L,mid);
rs[p]=Merge(rs[x],rs[y],mid+1,R);
w[p]=w[ls[p]]+w[rs[p]];
return p;
}
int Ask(int x,int L,int R,int l,int r){
if(!x)return 0;
if(L==l&&R==r)return w[x];
int mid=(L+R)>>1;
if(r<=mid)return Ask(ls[x],L,mid,l,r);
if(l>mid)return Ask(rs[x],mid+1,R,l,r);
return Ask(ls[x],L,mid,l,mid)+Ask(rs[x],mid+1,R,mid+1,r);
}
}T;
void Insert(int c){
int p=last,np=last=++cnt;
len[np]=len[p]+1;
for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
if(!p)fa[np]=1;
else{
int q=ch[p][c];
if(len[p]+1==len[q])fa[np]=q;
else{
int nq=++cnt;len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[nq]));
fa[nq]=fa[q];pos[nq]=pos[q];fa[q]=fa[np]=nq;
for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
}
}
return;
}
int main()
{
scanf("%d",&n);cnt=last=1;
scanf("%s",s+1);
for(int i=1;i<=n;i++)
Insert(s[i]-'a'),rt[last]=T.Change(rt[last],1,n,i),pos[last]=i;
for(int i=1;i<=cnt;i++)c[len[i]]++;
for(int i=1;i<=n;i++)c[i]+=c[i-1];
for(int i=1;i<=cnt;i++)p[c[len[i]]--]=i;
for(int i=cnt;i>1;i--)
rt[fa[p[i]]]=T.Merge(rt[fa[p[i]]],rt[p[i]],1,n);
int ans=1;
for(int i=2;i<=cnt;i++){
int x=p[i],y=fa[x];
if(y==1){f[x]=1;top[x]=x;continue;}
y=top[y];
if(T.Ask(rt[y],1,n,pos[x]-len[x]+len[y],pos[x]-1))
f[x]=f[y]+1,top[x]=x;
else top[x]=y,f[x]=f[y];
ans=max(ans,f[x]);
}
printf("%d\n",ans);
return 0;
}
CF700E-Cool Slogans【SAM,线段树合并,dp】的更多相关文章
- CF700E:Cool Slogans(SAM,线段树合并)
Description 给你一个字符串,如果一个串包含两个可有交集的相同子串,那么这个串的价值就是子串的价值+1.问你给定字符串的最大价值子串的价值. Input 第一行读入字符串长度$n$,第二行是 ...
- CF700E Cool Slogans——SAM+线段树合并
RemoteJudge 又是一道用线段树合并来维护\(endpos\)的题,还有一道见我的博客CF666E 思路 先把\(SAM\)建出来 如果两个相邻的串\(s_i\)和\(s_{i+1}\)要满足 ...
- Codeforces 700E. Cool Slogans 字符串,SAM,线段树合并,动态规划
原文链接https://www.cnblogs.com/zhouzhendong/p/CF700E.html 题解 首先建个SAM. 一个结论:对于parent树上任意一个点x,以及它所代表的子树内任 ...
- Codeforces.700E.Cool Slogans(后缀自动机 线段树合并 DP)
题目链接 \(Description\) 给定一个字符串\(s[1]\).一个字符串序列\(s[\ ]\)满足\(s[i]\)至少在\(s[i-1]\)中出现过两次(\(i\geq 2\)).求最大的 ...
- CF1037H Security——SAM+线段树合并
又是一道\(SAM\)维护\(endpos\)集合的题,我直接把CF700E的板子粘过来了QwQ 思路 如果我们有\([l,r]\)对应的\(SAM\),只需要在上面贪心就可以了.因为要求的是字典序比 ...
- 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree
原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...
- UOJ#395. 【NOI2018】你的名字 字符串,SAM,线段树合并
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ395.html 题解 记得同步赛的时候这题我爆0了,最暴力的暴力都没调出来. 首先我们看看 68 分怎么做 ...
- loj#2059. 「TJOI / HEOI2016」字符串 sam+线段树合并+倍增
题意:给你一个子串,m次询问,每次给你abcd,问你子串sa-b的所有子串和子串sc-d的最长公共前缀是多长 题解:首先要求两个子串的最长公共前缀就是把反过来插入变成最长公共后缀,两个节点在paren ...
- 2019.02.27 bzoj4556: [Tjoi2016&Heoi2016]字符串(二分答案+sam+线段树合并)
传送门 题意:给一个字符串SSS. 有mmm次询问,每次给四个参数a,b,c,da,b,c,da,b,c,d,问s[a...b]s[a...b]s[a...b]的所有子串和s[x...y]s[x... ...
随机推荐
- C#设计模式---迭代器模式(Iterator Pattern)
一.目的 提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节. 二.定义 迭代器模式提供了一种方法访问一个聚合对象(理解为集合对象)中各个元素,而又无需暴露该对象的内部表示,这样既可 ...
- linux centos 设置笔记本合盖不待机
1.设置笔记本合盖不待机 打开配置文件 vi /etc/systemd/logind.conf 将 HandleLidSwitch 变量前的注释 # 去掉 修改 HandleLidSwitch 变量参 ...
- 【CSS复合选择器、元素显示模式、背景】前端小抄(3) - Pink老师自学笔记
[CSS复合选择器.元素显示模式.背景]前端小抄(3) 本学习笔记是个人对 Pink 老师课程的总结归纳,转载请注明出处! 一.CSS的复合选择器 1.1 什么是复合选择器 在 CSS 中,可以根据选 ...
- golang——rune
byte 等同于int8,常用来处理ascii字符 rune等同于int32,常用来处理unicode或utf-8字符//可以处理中文
- 16 bit 的灰度图如何显示
16 bit 的灰度图如何在QT中显示 用Mat构造的 16 bit 灰度图 无法直接显示,需要转换成 8 bit 的灰度图在QT中显示, 使用OpenCV自带的最大最小值归一法, cv::norma ...
- git clone 设置临时的 proxy
export ALL_PROXY=socks5://127.0.0.1:1086 git clone --depth 1 https://github.com/xxx/xxx.git unset AL ...
- 手动编译部署LNMP环境(CentOS7.5+Nginx-1.18.0+MySQL-5.7.30+PHP-7.4.14)
在平时运维工作中,经常需要用到LNMP应用框架.LNMP环境是指在Linux系统下,由Nginx + MySQL + PHP组成的网站服务器架构. 可参考前面的文章: 如何在CentOS 7上搭建LA ...
- 区间DP的瞎扯淡
写在前面连个引言都不加就直接开1. 区间DP状态常见模板: f[i][j]常常表示第i个到第j个这个区间内达到题目要求,所需要的最小值(最大值) 如: 1. [石子合并](https://www.lu ...
- ubantu硬盘不足,无法启动
我的ubantu虚拟机经过我一顿操作后,就起不来了.然后经过多方询问,广集天下良方,最终发现是由于分配的硬件空间不足导致的.现象如下: 通过查看 root@ubantu:/snap# df -h Fi ...
- VMware安装IPFire防火墙镜像
之后便可以通过WEB登录到管理页面(admin账号,密码是在上面配置的) 详细可参考:https://www.mobibrw.com/2016/4900