正题

题目链接: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. request库的简单使用

    7种基本用法 首先安装requests库 如果你本地只有一个python环境直接运行pip install requests 像我本地装了3个不同的python怎么使你安装在你想要安装的python下 ...

  2. mzy对于反射的复习

    反射其实就是指在超脱规则的束缚,从强引用到弱相关,在上帝视角做事情,对于写配置文件,和一些框架的源码,得到调用上至关重要,java带有解释器的语法特性,使得泛型一类的语法糖和反射配合之后更如鱼得水! ...

  3. 打印菱形图案(PTA)

    打印菱形图案 本题要求编写程序,打印一个高度为n的.由"*"组成的正菱形图案. 标题输入格式 输入在一行中给出一个正的奇数n. 输出格式 输出由n行星号"*"组 ...

  4. (原创)[C#] DataTable排序扩展方法

    一,前言 DataTable的应用极其广泛,对DataTable进行排序也有很多方式,每种的实现方式都不难,但是使用起来却比较繁琐,所以本人便写了一个扩展方法,专门对DataTable进行操作. 本篇 ...

  5. 并发编程之:Atomic

    大家好,我是小黑,一个在互联网苟且偷生的农民工. 在开始讲今天的内容之前,先问一个问题,使用int类型做加减操作是不是线程安全的呢?比如 i++ ,++i,i=i+1这样的操作在并发情况下是否会有问题 ...

  6. MySQL5.7.24 安装

    官网地址 https://dev.mysql.com/downloads/mysql/ 1. 安装依赖 yum install -y cmake make gcc gcc-c++ libaio ncu ...

  7. Cookie及通过Cookie常见应用

    会话的概念 会话可以简单理解为:用户打开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话. 会话需要解决的问题 每个用户与服务器进行交互的过程中,各自会有一 ...

  8. [考试总结]noip模拟46

    脑袋确实是不好使了需要回家暴颓治疗 数数数树鼠树 真好玩. 数数 大水题一个,妥妥的签到题目,然后... 我没签上 气展了!!! 其实我还是想麻烦了. 就是我们实际上就是排序之后每一次找头上和尾巴上的 ...

  9. LayoutControl控件使用

    因默认外边距过大需要将外边距缩小用以下代码实现layoutControlGroup1.Padding = DevExpress.XtraLayout.Utils.Padding.Empty;是否允许只 ...

  10. AQS深入分析

    一.node概念 1.当线程获取锁失败时,会被打包成一个node放到同步队列中 2.node属性 当线程获取锁失败时,会被打包成一个node放到同步队列中,所以node属性中有一个thread属性; ...