CF700E Cool Slogans 后缀自动机 + right集合线段树合并 + 树形DP
题目描述
给出一个长度为n的字符串s[1],由小写字母组成。定义一个字符串序列s[1....k],满足性质:s[i]在s[i-1] (i>=2)中出现至少两次(位置可重叠),问最大的k是多少,使得从s[1]开始到s[k]都满足这样一个性质。
发现 $s[1...k]$ 之间一定是互为后缀关系. 那么就可以建出后缀树,令 $dp_{u}$ 表示 $u$ 节点代表子串的答案
维护 $top_{u}$ 表示 $u$ 以及 $u$ 在后缀树的祖先中合法的且答案最大(答案相同则最短)的节点编号
$dp_{u}\Rightarrow dp_{top_{fa}}+1$ ,$fa$ 在 $u$ 中出现大于等于2次
$dp_{u}=1,top_{u}=top_{fa}$,$fa$ 在 $u$ 中仅出现 $1$ 次
#include<bits/stdc++.h>
#define maxn 400002
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
namespace tr
{
#define lson t[x].l
#define rson t[x].r
#define mid ((l+r)>>1)
int cnt;
struct Node { int l,r; }t[maxn*20];
void modify(int &x,int l,int r,int k)
{
if(!x) x=++cnt;
if(l==r) return;
if(k<=mid) modify(lson,l,mid,k);
else modify(rson,mid+1,r,k);
}
int merge(int u,int v)
{
if(!u||!v) return u+v;
int x=++cnt;
lson=merge(t[u].l,t[v].l);
rson=merge(t[u].r,t[v].r);
return x;
}
int query(int x,int l,int r,int L,int R)
{
if(!x) return 0;
if(l>=L&&r<=R) return 1;
int tmp=0;
if(L<=mid) tmp+=query(lson,l,mid,L,R);
if(R>mid) tmp+=query(rson,mid+1,r,L,R);
return tmp;
}
};
namespace SAM
{
char str[maxn];
int last,tot,n;
int trans[maxn][27],f[maxn],len[maxn],C[maxn],rk[maxn],top[maxn],dp[maxn],pos[maxn],rt[maxn];
void init() { last=tot=1; }
void extend(int c,int i)
{
int np=++tot,p=last;
len[np]=len[p]+1,last=np;
while(p&&!trans[p][c]) trans[p][c]=np,p=f[p];
if(!p) f[np]=1;
else
{
int q=trans[p][c];
if(len[q]==len[p]+1) f[np]=q;
else
{
int nq=++tot;
len[nq]=len[p]+1;
pos[nq]=pos[q];
memcpy(trans[nq],trans[q],sizeof(trans[q]));
f[nq]=f[q], f[np]=f[q]=nq;
while(p&&trans[p][c]==q) trans[p][c]=nq,p=f[p];
}
}
pos[np]=i;
tr::modify(rt[last],1,n,i);
}
void prepare()
{
int i,j;
init();
scanf("%d%s",&n,str+1);
for(i=1;i<=n;++i) extend(str[i]-'a',i);
for(i=1;i<=tot;++i) ++C[len[i]];
for(i=1;i<=tot;++i) C[i]+=C[i-1];
for(i=1;i<=tot;++i) rk[C[len[i]]--]=i;
for(i=tot;i>1;--i)
{
int u=rk[i];
rt[f[u]]=tr::merge(rt[f[u]],rt[u]);
}
}
void calc()
{
int i,j,ans=1;
for(i=2;i<=tot;++i)
{
int u=rk[i],ff=f[rk[i]];
if(ff==1) { top[u]=u,dp[u]=1; continue; }
if(tr::query(rt[top[ff]],1,n,pos[u]-len[u]+len[top[ff]],pos[u]-1))
top[u]=u,dp[u]=dp[top[ff]]+1;
else
top[u]=top[ff];
ans=max(ans,dp[u]);
}
printf("%d\n",ans);
}
};
int main()
{
//msetIO("input");
SAM::prepare();
SAM::calc();
return 0;
}
CF700E Cool Slogans 后缀自动机 + right集合线段树合并 + 树形DP的更多相关文章
- CF1037H Security 后缀自动机 + right集合线段树合并 + 贪心
题目描述: 给定一个字符串 $S$ 给出 $Q$ 个操作,给出 $L,R,T$,求出字典序最小的 $S_{1}$ 为 $S[L...R]$的子串,且 $S_{1}$ 的字典序严格大于 $T$. 输出这 ...
- [BJWC2018]Border 的四种求法(后缀自动机+链分治+线段树合并)
题目描述 给一个小写字母字符串 S ,q 次询问每次给出 l,r ,求 s[l..r] 的 Border . Border: 对于给定的串 s ,最大的 i 使得 s[1..i] = s[|s|-i+ ...
- 【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并
题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r ...
- CF547E Milk and Friends(AC自动机的fail指针上建主席树 或 广义后缀自动机的parent线段树合并)
What-The-Fatherland is a strange country! All phone numbers there are strings consisting of lowercas ...
- LOJ #2537. 「PKUWC 2018」Minimax (线段树合并 优化dp)
题意 小 \(C\) 有一棵 \(n\) 个结点的有根树,根是 \(1\) 号结点,且每个结点最多有两个子结点. 定义结点 \(x\) 的权值为: 1.若 \(x\) 没有子结点,那么它的权值会在输入 ...
- 【CF700E】Cool Slogans 后缀自动机+线段树合并
[CF700E]Cool Slogans 题意:给你一个字符串S,求一个最长的字符串序列$s_1,s_2,...,s_k$,满足$\forall s_i$是S的子串,且$s_i$在$s_{i-1}$里 ...
- 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)
模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...
- Codeforces.700E.Cool Slogans(后缀自动机 线段树合并 DP)
题目链接 \(Description\) 给定一个字符串\(s[1]\).一个字符串序列\(s[\ ]\)满足\(s[i]\)至少在\(s[i-1]\)中出现过两次(\(i\geq 2\)).求最大的 ...
- 洛谷P4770 [NOI2018]你的名字 [后缀自动机,线段树合并]
传送门 思路 按照套路,直接上后缀自动机. 部分分:\(l=1,r=|S|\) 首先把\(S\)和\(T\)的后缀自动机都建出来. 考虑枚举\(T\)中的右端点\(r\),查询以\(r\)结尾的串最长 ...
随机推荐
- c#处理bin文件
1. fs.Position 写入的位置,从哪个位置开始写 fs.Write(byte1,0,byte1.Length); byte1写入的byte[], 写入内容从第几位开始取,length取多长 ...
- Java ——方法
本节重点思维导图 方法的定义 例题:1!+2!+3!+4!+…..+15!=? public class Demo { public static void main(String[] args) ...
- PHPstorm Xdebugger最全详细
0 Xdebug调试的原理(选看) 图0-1 单机调试原理示意图 图0-2 多机调试原理示意图 对于PHP开发,初来咋到,开发环境的搭建和理解感觉是最烦人的一件事了.不像JAVA,打开一个Eclips ...
- 剑指offer--day04
1.1题目:变态跳台阶:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级.求该青蛙跳上一个n级的台阶总共有多少种跳法. 1.2解题思路: 当n=1时,结果为1: 当n=2时,结果为2: ...
- chrome:// .......命令 集结
Chrome 有很多的特性在界面菜单中是没有体现的,可以通过 chrome:// 命令来访问 我搜集了下面这些!!!当然也是在网上找的!有的我自己也不知道是什么,具体作用是什么!还是等高人来探讨吧!c ...
- 20190902 On Java8 第十六章 代码校验
第十六章 代码校验 你永远不能保证你的代码是正确的,你只能证明它是错的. 测试 测试覆盖率的幻觉 测试覆盖率,同样也称为代码覆盖率,度量代码的测试百分比.百分比越高,测试的覆盖率越大. 当分析一个未知 ...
- string类find_first_not_of ()方法
string类find_first_not_of ()方法 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://xfqxj.blog. ...
- HIbernate入门3
HIbernate的一对多操作: 1. 创建实体类:一个Customer类(客户类)和一个LinkMan类(联系人),两者的关系为:一个客户中可能有多个联系人(关于一对多的实体类之间的关联,不做详细介 ...
- 在VS Code中使用Jupyter Notebook
一.安装配置 1.在扩展商店中安装官方的Python扩展包 2.系统已经安装了Jupyter Notebook 由于系统上的Python环境是用Anaconda安装的,已经有Jupyter Noteb ...
- 解决MarkDown打开出现:awesomium web-brower framework This view has crashed
当在windows 8 以上操作系统安装markdown 的时候,可能会出现这样的错误 解决方法: 官网链接:http://markdownpad.com/faq.html#livepreview-d ...