Description

给你一个字符串,如果一个串包含两个可有交集的相同子串,那么这个串的价值就是子串的价值+1。问你给定字符串的最大价值子串的价值。

Input

第一行读入字符串长度$n$,第二行是字符串。

Output

一行答案。

Sample Input1

3
abc

Sample Output1

1

Sample Input2

5
ddddd

Sample Output2

5

Sample Input3

11
abracadabra

Sample Output3

3

Solution

首先把后缀树建立出来,然后从下往上线段树合并一下$endpos$。

设$f[i]$表示从后缀树的根$DP$到了$i$节点的最大价值,$top[i]$表示$i$节点是从哪个节点转移来的。

如果父亲代表的字符串在当前节点代表的字符串中出现了两次及以上,那么就$f[x]=f[fa]+1,top[x]=x$

否则$f[x]=f[fa],top[x]=top[fa]$

父亲代表的字符串在当前节点代表的字符串中出现的次数可以直接根据$SAM$的$step$数组和线段树合并出的$endpos$什么的直接判断一下。

Code

 #include<iostream>
#include<cstring>
#include<cstdio>
#define N (800009)
using namespace std; struct Sgt{int ls,rs,val;}Segt[N<<];
struct Edge{int to,next;}edge[N<<];
int n,ans,sgt_num,Root[N],f[N],top[N];
int head[N],num_edge;
int last=,p,q,np,nq,cnt=;
int fa[N],son[N][],step[N],pos[N];
char s[N]; void add(int u,int v)
{
edge[++num_edge].to=v;
edge[num_edge].next=head[u];
head[u]=num_edge;
} void Insert(int x,int r)
{
p=last; np=last=++cnt;
step[np]=step[p]+; pos[np]=r;
while (p && !son[p][x]) son[p][x]=np, p=fa[p];
if (!p) fa[np]=;
else
{
q=son[p][x];
if (step[q]==step[p]+) fa[np]=q;
else
{
nq=++cnt; step[nq]=step[p]+; pos[nq]=r;
memcpy(son[nq],son[q],sizeof(son[q]));
fa[nq]=fa[q]; fa[q]=fa[np]=nq;
while (son[p][x]==q) son[p][x]=nq, p=fa[p];
}
}
} void Update(int &now,int l,int r,int x)
{
if (!now) now=++sgt_num;
Segt[now].val++;
if (l==r) return;
int mid=(l+r)>>;
if (x<=mid) Update(Segt[now].ls,l,mid,x);
else Update(Segt[now].rs,mid+,r,x);
} int Merge(int x,int y)
{
if (!x || !y) return x|y;
int now=++sgt_num;
Segt[now].ls=Merge(Segt[x].ls,Segt[y].ls);
Segt[now].rs=Merge(Segt[x].rs,Segt[y].rs);
Segt[now].val=Segt[x].val+Segt[y].val;
return now;
} int Query(int now,int l,int r,int l1,int r1)
{
if (!now) return ;
if (l>r1 || r<l1) return ;
if (l1<=l && r<=r1) return Segt[now].val;
int mid=(l+r)>>;
return Query(Segt[now].ls,l,mid,l1,r1)+Query(Segt[now].rs,mid+,r,l1,r1);
} void DFS(int x)
{
if (pos[x]) Update(Root[x],,n,pos[x]);
for (int i=head[x]; i; i=edge[i].next)
{
DFS(edge[i].to);
Root[x]=Merge(Root[x],Root[edge[i].to]);
}
} void DP(int x)
{
for (int i=head[x]; i; i=edge[i].next)
{
int y=edge[i].to;
if (x==) f[y]=, top[y]=y;
else if (Query(Root[top[x]],,n,pos[y]-step[y]+step[top[x]],pos[y]-))
f[y]=f[x]+, top[y]=y;
else f[y]=f[x], top[y]=top[x];
DP(edge[i].to);
ans=max(ans,f[edge[i].to]);
}
} int main()
{
scanf("%d%s",&n,s);
for (int i=; i<n; ++i) Insert(s[i]-'a',i+);
for (int i=; i<=cnt; ++i) add(fa[i],i);
DFS(); DP();
printf("%d\n",ans);
}

CF700E:Cool Slogans(SAM,线段树合并)的更多相关文章

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

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

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

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

  3. CF700E-Cool Slogans【SAM,线段树合并,dp】

    正题 题目链接:https://www.luogu.com.cn/problem/CF700E 题目大意 给出一个字符串\(S\),求一个最大的\(k\)使得存在\(k\)个字符串其中\(s_1\)是 ...

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

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

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

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

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

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

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

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

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

  9. [NOI2018]你的名字(SAM+线段树合并)

    考虑l=1,r=n的68分,对S和T建SAM,对T的SAM上的每个节点,计算它能给答案带来多少贡献. T上节点x代表的本质不同的子串数为mx[x]-mx[fa[x]],然后需要去掉所代表子串与S的最长 ...

随机推荐

  1. Oracle简介及安装

    一.轨迹 二.Oracle简介 Oracle是现在全世界最大的数据库提供商,编程语言提供商,应用软件提供商,它的地位等价于微软的地位. Oracle在古希腊神话中被称为“神谕”,指的是上帝的宠儿,在中 ...

  2. A计划 hdu2102(bfs一般题)

    A计划 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submis ...

  3. 设计模式原则(1)--Single Responsibility Principle(SRP)--单一职责原则

    1.定义: 不要存在多于一个导致类变更的原因.通俗的说,即一个类只负责一项职责.  2.使用场景: 如果类A有两个职责:d1,d2.当职责d1需要修改时,可能会导致原本运行正常的职责d2功能产生问题. ...

  4. nodejs 知识总结

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.添加模块:保存到package.json文件中; # npm install vue --save    注意 ...

  5. 第三十天- 进程 Process模块 空间隔离

    1.进程: 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.在早期面向进程设计的计算机结构中,进程是程序的基本执行实体: ...

  6. InfoQ观察:Java EE的未来

    原创 2017-03-06 Charles Humble 聊聊架构 作者|Charles Humble编辑|薛命灯 作为InfoQ下一年编辑关注点审核工作的一部分,我们挑选了Java作为深入探讨的主题 ...

  7. Android 投射工具和录屏工具

    前言 写博客,难免需要上传手机截图或者是Gif文件,分享两个常用的工具 GifCam GifCam,一个简单的 屏幕录制工具,可以实现Gif的录制编辑和保存 Total Control Total C ...

  8. apt-get update 系列作用

    sudo apt-get update 更新源 sudo apt-get upgrade 更新已安装的包 sudo apt-get dist-upgrade 升级系统 下面摘自知乎用户回答: apt- ...

  9. Maven学习(二)使用命令创建maven项目

    创建maven项目 手动 严格参照约定目录结构,我们开始手动新增文件夹 命令方式 project项目 我们也可以使用maven自动生成目录: mvn archetype:generate -Dgrou ...

  10. Python+Selenium笔记(五):生成测试报告

    #HTMLTestRunner代码修改参考 微微微笑 的说明,下面是链接,这个已经说的很详细了 https://www.cnblogs.com/miniren/p/5301081.html (一) 前 ...