正题

题目链接:https://www.luogu.com.cn/problem/CF700E


题目大意

给出一个字符串\(S\),求一个最大的\(k\)使得存在\(k\)个字符串其中\(s_1\)是\(S\)的子串,\(s_{i+1}\)在\(s_i\)中出现了至少\(2\)次。


解题思路

首先我们需要有两个结论

  1. \(s_{i+1}\)一定是\(s_i\)的其中一个后缀。因为如果\(s_{i+1}\)不是\(s_i\)的一个后缀,那么\(s_i\)去掉后面那一部分不会影响匹配数并且更短,也就是更优
  2. 对于\(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】的更多相关文章

  1. CF700E:Cool Slogans(SAM,线段树合并)

    Description 给你一个字符串,如果一个串包含两个可有交集的相同子串,那么这个串的价值就是子串的价值+1.问你给定字符串的最大价值子串的价值. Input 第一行读入字符串长度$n$,第二行是 ...

  2. CF700E Cool Slogans——SAM+线段树合并

    RemoteJudge 又是一道用线段树合并来维护\(endpos\)的题,还有一道见我的博客CF666E 思路 先把\(SAM\)建出来 如果两个相邻的串\(s_i\)和\(s_{i+1}\)要满足 ...

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

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

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

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

  5. CF1037H Security——SAM+线段树合并

    又是一道\(SAM\)维护\(endpos\)集合的题,我直接把CF700E的板子粘过来了QwQ 思路 如果我们有\([l,r]\)对应的\(SAM\),只需要在上面贪心就可以了.因为要求的是字典序比 ...

  6. 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree

    原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...

  7. UOJ#395. 【NOI2018】你的名字 字符串,SAM,线段树合并

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ395.html 题解 记得同步赛的时候这题我爆0了,最暴力的暴力都没调出来. 首先我们看看 68 分怎么做 ...

  8. loj#2059. 「TJOI / HEOI2016」字符串 sam+线段树合并+倍增

    题意:给你一个子串,m次询问,每次给你abcd,问你子串sa-b的所有子串和子串sc-d的最长公共前缀是多长 题解:首先要求两个子串的最长公共前缀就是把反过来插入变成最长公共后缀,两个节点在paren ...

  9. 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... ...

随机推荐

  1. TP6 服务器响应500时没有错误信息的解决方案

    重点!!!! 首先,确认你的电脑管理员账户是否含有中文!!!!!!就像下面这种:所以出现了没有错误提示    查看nginx日志显示\vendor\topthink\framework\src\thi ...

  2. 05.SpringMVC之请求映射

    @RequestMapping是一个用来处理请求地址映射的注解,可用于类或者方法上.用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径. @RequestMapping注解有六个属性,下面进 ...

  3. 【SOE】 ArcGIS Server对象扩展(SOE)开发注意事项

    ArcGIS  Server对象扩展(SOE)开发注意事项 1.SOE介绍 在ArcGIS 10.1中ArcGIS Server不在支持DCOM方式的连接,这也就意味着我们不能通过本地方式的连接使用A ...

  4. Mybatis出现错误org.apache.ibatis.executor.ExecutorException: No constructor found in

    错误显示没有发现构造器. 其实就是重写了构造器后,忘了补写一个默认的空参构造器了.此类的错误还经常出现在spring等这种大量使用反射的框架中.因为这些框架在调用反射的类后会默认调用默认的构造器 解决 ...

  5. k8s 探针 exec多个判断条件条件 多个检测条件

    背景 1,之前我们的yaml文件里面有就绪探针. 2,探针是检测一个文件是否生成,生成了说明服务正常. 3,现在要加一个检测,也是一个文件是否存在并且不为空. 4,只有两个条件同时满足了 服务才算正常 ...

  6. Ecplise项目导入IDEA(纯小白名词解释)

    1. Module 模块 一个大的项目不仅仅是只有Java的源文件,还有数据库,服务器,web等等文件一起使用,将类似于这样分类的文件定义为 module 例如 core Module(核心).web ...

  7. Qt编译工程提示qt creator no rule to make target opencv2/core/hal/interface.h need by debug解决方法

    总是提示 qt creator no rule to make target opencv2/core/hal/interface.h need by debug解决方法: 也算是花了整整两个小时踩坑 ...

  8. go语言学习代码

    1.day01 package main //声明文件所在的包,每个go文件必须有归属包 import "fmt" //引入程序中需要用的包,为了使用包下的函数 比如函数:Prin ...

  9. 性能测试必备命令(1)- free

    性能测试必备的 Linux 命令系列,可以看下面链接的文章哦 https://www.cnblogs.com/poloyy/category/1819490.html 介绍 显示系统的内存使用情况 语 ...

  10. shell脚本中的多行注释

    shell 中注释的使用方法 1. 单行注释 单行注释最为常见,它是通过一个'#'来实现的.注意shell脚本的最开始部分"#!/bin/bash"的#号不是用来注释的. 2. 多 ...