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. 无责任Windows Azure SDK .NET开发入门篇(一):开发前准备工作

    Windows Azure开发前准备工作 什么是 Azure SDK for .NET?微软官方告诉我们:Azure SDK for .NET 是一套应用程序,其中包括 Visual Studio 工 ...

  2. MyCAT全局序列号-数据库方式

    1.MyCat中的全局序列号介绍 在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一.为此,MyCat 提供了全局 sequence,并且提供了包含本地配置和数据库配置等多种实现方式. ...

  3. SpringCloud高可用和高并发

    1 高可用 什么是高可用:(High Availability)在一个长时间内服务不受影响.通俗的讲就是,一个机器挂掉的时候,有其他机器可以继续提供同样的服务. 如何实现高可用:冗余+自动故障转移.冗 ...

  4. java 传入list集合 返回树形菜单,for循环遍历

    public List<SysPermissionVO> getTreeMenu(List<SysPermissionVO> list,SysPermissionVO sysP ...

  5. JAVA设计模式详解(六)----------状态模式

    各位朋友,本次LZ分享的是状态模式,在这之前,恳请LZ解释一下,由于最近公司事情多,比较忙,所以导致更新速度稍微慢了些(哦,往后LZ会越来越忙=.=). 状态模式,又称状态对象模式(Pattern o ...

  6. JavaScript--DOM元素尺寸和位置(22)

    一 获取元素的CSS大小 1.通过style内联获取元素的大小 var box = document.getElementById('box'); // 获得元素; box.style.width; ...

  7. 使用Apache php 的一些基本操作(一)

    切换目录命令:(就可以在www文件夹里面操作了) cd /var/www/html 新建一个文件: sudo vim info.php (这里出现了一个问题,sudo: vim: command no ...

  8. 小记SharePoint REST API Search和COM

    1.管理员身份Visual Studio,新建类项目 SPCOM 2.编写逻辑实现代码 重点关注搜索结果的属性包括: Title,Author,Path,Description,HitHighligh ...

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

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

  10. Python 基于Python实现邮件发送

    基于Python实现邮件发送   by:授客 QQ:1033553122 测试环境: Python版本:Python 2.7   注:需要修改mimetypes.py文件(该文件可通过文章底部的网盘分 ...