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. CSS 样式属性

    大小 width   宽               body {   min-width:1200px; height  高                      }               ...

  2. Java基础——网络编程(三)

    TCP 网络编程 -- tcp 分为客户端和服务端 -- 客户端对应的对象是 Socket -- 服务端对应的对象是 ServerSocket -- 如果客户端先启动,则出现 connection r ...

  3. sql语句之where子句

    现在的登录都是把信息存在数据库,然后把输入的与数据库内容进行匹配,一样就登录成功,否则不成功.验证码是为了防止暴力破解,因为计算机能够自动匹配密码,但是不能识别图片上的字母,只有人能识别,所以匹配的速 ...

  4. 如何在SpringMVC中使用REST风格的url

    如何在SpringMVC中使用REST风格的url 1.url写法: get:/restUrl/{id} post:/restUrl delete:/restUrl/{id} put:/restUrl ...

  5. Linux常用基本命令:uniq-去重复

    uniq命令 作用:输出或者忽略文件中的重复行 格式: uniq [option] [file|stdin] ghostwu@dev:~/linux/uniq$ cat ghostwu.txt 192 ...

  6. JQ面试问题(转载)

    1 你在公司是怎么用jquery的? 答:在项目中是怎么用的是看看你有没有项目经验(根据自己的实际情况来回答) 你用过的选择器啊,动画啊,表单啊,ajax事件等 配置Jquery环境 下载jquery ...

  7. javascript原型对象与原型链

    在javascript中,当系统加载构造函授后 ,会自动在内存中增加一个对象,这个对象就是原型对象.构造函数和原型对象在内存中表现为相互独立,但两者之间还存在联系,构造函数的prototype是原型对 ...

  8. 用node.js模拟服务器和客户端

    服务器 代码 var net = require("net") var server = net.createServer(); server.listen(12306," ...

  9. 31:字符串p型编码

    31:字符串p型编码 查看 提交 统计 提问 总时间限制:  1000ms 内存限制:  65536kB 描述 给定一个完全由数字字符('0','1','2',…,'9')构成的字符串str,请写出s ...

  10. HTTP协议学习随笔

    一 HTTP概述 HTTP简单说其实就是一套语言交流规则!Http使用的是可靠的数据传输协议,因此即使数据来自地球的另一端,也能够确保数据在传输过程中不会被损坏或产生混乱. B/S结构 用户在浏览器, ...