CF700E:Cool Slogans(SAM,线段树合并)
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,线段树合并)的更多相关文章
- CF700E Cool Slogans——SAM+线段树合并
RemoteJudge 又是一道用线段树合并来维护\(endpos\)的题,还有一道见我的博客CF666E 思路 先把\(SAM\)建出来 如果两个相邻的串\(s_i\)和\(s_{i+1}\)要满足 ...
- Codeforces 700E. Cool Slogans 字符串,SAM,线段树合并,动态规划
原文链接https://www.cnblogs.com/zhouzhendong/p/CF700E.html 题解 首先建个SAM. 一个结论:对于parent树上任意一个点x,以及它所代表的子树内任 ...
- CF700E-Cool Slogans【SAM,线段树合并,dp】
正题 题目链接:https://www.luogu.com.cn/problem/CF700E 题目大意 给出一个字符串\(S\),求一个最大的\(k\)使得存在\(k\)个字符串其中\(s_1\)是 ...
- CF1037H Security——SAM+线段树合并
又是一道\(SAM\)维护\(endpos\)集合的题,我直接把CF700E的板子粘过来了QwQ 思路 如果我们有\([l,r]\)对应的\(SAM\),只需要在上面贪心就可以了.因为要求的是字典序比 ...
- 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree
原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...
- UOJ#395. 【NOI2018】你的名字 字符串,SAM,线段树合并
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ395.html 题解 记得同步赛的时候这题我爆0了,最暴力的暴力都没调出来. 首先我们看看 68 分怎么做 ...
- loj#2059. 「TJOI / HEOI2016」字符串 sam+线段树合并+倍增
题意:给你一个子串,m次询问,每次给你abcd,问你子串sa-b的所有子串和子串sc-d的最长公共前缀是多长 题解:首先要求两个子串的最长公共前缀就是把反过来插入变成最长公共后缀,两个节点在paren ...
- 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... ...
- [NOI2018]你的名字(SAM+线段树合并)
考虑l=1,r=n的68分,对S和T建SAM,对T的SAM上的每个节点,计算它能给答案带来多少贡献. T上节点x代表的本质不同的子串数为mx[x]-mx[fa[x]],然后需要去掉所代表子串与S的最长 ...
随机推荐
- LINQ 图解 LINQ学习第三篇
LINQ,语言集成查询(Language INtegrated Query)是一组用于c#和Visual Basic语言的扩展.它允许编写C#或者Visual Basic代码以查询数据库相同的方式操作 ...
- EWS 通过SubscribeToPullNotifications订阅Exchange删除邮件
摘要 在使用拉通知的方式监听exchange邮件的时候,无法监听到收件箱删除的邮件.最后通过调试发现,在删除收件箱邮件的时候,是将收件箱的邮件移动到了deleted item文件夹,会触发Moved事 ...
- C#图片处理类
转载來源:简书 转载作者:幻凌风 转载来源:https://www.jianshu.com/p/e1bab83e87ce using System; using System.Collections; ...
- 转载-asp.net id 和name的区别
name 是名字id是唯一标识name原来是为了标识之用,但是现在根据规范,都建议用id来标识元素.但是name在以下用途是不能替代的:1. 表单(form)的控件名,提交的数据都用控件的name而不 ...
- php魔术方法__tostring的应用
当echo一个对象的时候,会报错误 Object of class Person could not be converted to string 我们可以通过魔术方法__tostring() 把对 ...
- HDU6152
Friend-Graph Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tot ...
- DRF序列化
1. 安装 pip install djangoframework 2. app注册 rest_framework INSTALLED_APPS = [ 'django.contrib.admin', ...
- Java 基础知识总结 3
13.java类集 类集实际上是一个动态的对象数组,与一般的对象数组不同,类集中的对象内容可以任意扩充. 类集的特征: 1)这种框架是高性能的 2)框架必须允许不同类型的类集以相同的方式和高度互操作方 ...
- js 动态声明变量
var object = {}; for(var i=0; i<5; i++){ object['attr'+i] = i; }
- canvas绘画交叉波浪
做个记录,自己写的动态效果,可能以后用的着呢: <!DOCTYPE html> <html> <head> <meta charset="UTF-8 ...