bzoj 3676 [Apio2014]回文串(Manacher+SAM)
【题目链接】
http://www.lydsy.com/JudgeOnline/problem.php?id=3676
【题意】
给定一个字符串,定义一个串的权值为长度*出现次数,求最大权的回文子串。
【思路】
马拉车求出本质不同的回文子串。
对于一个回文子串,在SAM中用倍增法在O(logn)的时间得到它的出现次数,即SAM中每个节点的right集大小,倍增数组和right都可以通过提前处理得到。
更新答案即可。
先来考虑一个简单的问题:
给出一个串S(|S|<=1000000)和M个询问,每次询问S中[si,ti]这一段串在总串中出现过几次。显然我们可以建出后缀数组并用二分+ST表简单地完成。但如果我们一定要用后缀自动机的知识呢?我们会发现,倘若我们能快速找到一个节点(状态)表示当前s~t这一段,只需直接调用它的size即可。(有关size的预处理:在SAM建立好之后,用所有点的size去累加它的parent的size)那么如何快速找到这样一个状态呢?首先SAM有一个性质:把每一个节点向它的parent连边,得到的树是原串的逆序串的后缀树。(只是这棵后缀树压缩后的边权都不知道)也就是说,如果我们构建出一棵SAM,它将同时有后缀树和trie的性质。举个例子,比如字符串baabaaa。设询问为s=5,t=6首先画出对应的后缀树(空节点不再画出):注意此时后缀树中“浅”的点表示的是“后缀”。我们先跑出从1开始到t的状态s,此时设我们在SAM中的节点p。样例里p对应在后缀树的最下面那个点。但是我们发现1~t的状态太长了,我们只需要s~t的状态。这样我们可以在这棵后缀树上倍增,能往某个祖先跑就往某个祖先跑。能跑的依据就是该祖先的深度>=t-s+1这样,我们就跑到了p节点在后缀树上的父亲的父亲,然后直接在SAM里调用它的信息即可。这里还有一个细节问题:如果询问是s=3,t=6,应该返回哪个点呢?这个时候发现不能完全覆盖,要不一个点少一些,要不一个点多一些。显然要把剩下的部分也选进去,也就是说仍然在p这个点。合法性显然,且可以证明这样最优。Quote from Here
【代码】
#include<set>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define trav(u,i) for(int i=front[u];i;i=e[i].nxt)
#define FOR(a,b,c) for(int a=(b);a<=(c);a++)
#define rep(a,b,c) for(int a=(b);a>=(c);a--)
using namespace std; typedef long long ll;
const int N = 6e5+;
const int D = ; char s[N];
int n,p[N]; struct SAM
{ ll ans;
int sz,last,ch[N][],fa[N],R[N],pos[N],l[N],b[N],cnt[N],fat[N][D];
SAM()
{
sz=ans=; last=++sz;
memset(cnt,,sizeof(cnt));
memset(R,,sizeof(R));
memset(fat,,sizeof(fat));
}
void add(int c,int id)
{
int np=++sz,p=last; last=np;
l[np]=l[p]+; R[np]=; pos[id]=last;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=;
else {
int q=ch[p][c];
if(l[q]==l[p]+) fa[np]=q;
else {
int nq=++sz; l[nq]=l[p]+;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
void get_pre()
{
FOR(i,,sz) cnt[l[i]]++;
FOR(i,,n) cnt[i]+=cnt[i-];
rep(i,sz,) b[cnt[l[i]]--]=i;
rep(i,sz,) R[fa[b[i]]]+=R[b[i]]; FOR(i,,sz) {
fat[i][]=fa[i];
FOR(j,,D-)
fat[i][j]=fat[fat[i][j-]][j-];
}
}
void get_ans(int u,int v)
{
int x=pos[v];
for(int i=D-;i>=;i--) {
int t=fat[x][i];
if(l[t]>=v-u+) x=t;
}
ans=max(ans,(ll)R[x]*(v-u+));
} } sam; void Manacher()
{
int mx=,id;
for(int i=;i<=n;i++) {
if(mx>i) p[i]=min(mx-i,p[*id-i-]);
else p[i]=;
while(s[i+p[i]+]==s[i-p[i]]) {
p[i]++;
sam.get_ans(i-p[i]+,i+p[i]);
}
if(p[i]+i>mx) mx=p[i]+i,id=i;
}
mx=;
for(int i=;i<=n;i++) {
if(mx>i) p[i]=min(mx-i-,p[*id-i]);
else p[i]=,sam.get_ans(i,i);
while(s[i+p[i]]==s[i-p[i]]) {
p[i]++;
sam.get_ans(i-p[i]+,i+p[i]-);
}
if(p[i]+i>mx) mx=p[i]+i,id=i;
}
} int main()
{
scanf("%s",s+);
n=strlen(s+);
FOR(i,,n) sam.add(s[i]-'a',i);
sam.get_pre();
s[]='+',s[n+]='-';
Manacher();
printf("%lld",sam.ans);
return ;
}
bzoj 3676 [Apio2014]回文串(Manacher+SAM)的更多相关文章
- 【BZOJ 3676】 3676: [Apio2014]回文串 (SAM+Manacher+倍增)
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 2343 Solved: 1031 Description 考 ...
- [BZOJ3676][APIO2014]回文串(Manacher+SAM)
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 3097 Solved: 1408[Submit][Statu ...
- BZOJ 3676: [Apio2014]回文串
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 2013 Solved: 863[Submit][Status ...
- bzoj 3676: [Apio2014]回文串 回文自动机
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 844 Solved: 331[Submit][Status] ...
- BZOJ 3676 [Apio2014]回文串 (后缀自动机+manacher/回文自动机)
题目大意: 给你一个字符串,求其中回文子串的长度*出现次数的最大值 明明是PAM裸题我干嘛要用SAM做 回文子串有一个神奇的性质,一个字符串本质不同的回文子串个数是$O(n)$级别的 用$manach ...
- bzoj 3676: [Apio2014]回文串【后缀自动机+manacher】
用manacher找出本质不同的回文子串放在SAM上跑 #include<iostream> #include<cstdio> #include<cstring> ...
- ●BZOJ 3676 [Apio2014]回文串
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3676 题解: 后缀数组,Manacher,二分 首先有一个结论:一个串的本质不同的回文串的个 ...
- bzoj 3676: [Apio2014]回文串【回文自动机】
回文自动机板子 或者是SAM+manacher+倍增,就是manacher求本质不同回文串(让f++的串),然后在SAM倍增查询对应点出现次数 #include<iostream> #in ...
- 字符串(马拉车算法,后缀数组,稀疏表):BZOJ 3676 [Apio2014]回文串
Description 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的“出 现值”为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最 大出现值. Input 输入只有一行 ...
- BZOJ 3676 [Apio2014]回文串(回文树)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3676 [题目大意] 考虑一个只包含小写拉丁字母的字符串s. 我们定义s的一个子串t的& ...
随机推荐
- js 对象的合并(3种方法)转载
对象的合并 需求:设有对象 o1 ,o2,需要得到对象 o3 var o1 = { a:'a' }, o2 = { b:'b' }; // 则 var o3 = { a:'a', b:'b' } 方法 ...
- Apache优化之多路处理模块理解
前言: 当项目被多人访问时导致访问数度变慢,查了许多资料,了解到Apache的核心模块——MPM(多路处理访问模块).在此对MPM的一些知识点进行整理. MPM_WINNT模块 windows系统使用 ...
- SpannableString的基本用法
原文地址:http://www.cnblogs.com/kross/p/3645594.html 以前一直好奇QQ的输入框里面是如何出现表情的,今天看了下这个,心中发出“原来是这样啊”的感叹. 通常情 ...
- BZOJ5101 POI2018Powódź(并查集)
如果某个格子的积水量超过了该格子的某个挡板高度,那么挡板另一端的积水量就会与其相同.看起来是一个不断合并的过程,考虑并查集.枚举深度,维护每个连通块内的方案数,深度超过某挡板高度时,将两端的连通块合并 ...
- 【spring学习笔记二】Bean
### bean的三种实例化方式: 1.构造 2.静态工厂 3.实例工厂 其中,工厂就是工厂的概念,工厂函数factor-method会返回她生产出来的产品类. 而构造初始化也可以选择初始化方式和销毁 ...
- HBase介绍及简易安装
HBase简介 HBase是Apache Hadoop的数据库,能够对大型数据提供随机.实时的读写访问,是Google的BigTable的开源实现.HBase的目标是存储并处理大型的数据,更具体地说仅 ...
- 【bzoj4031】[HEOI2015]小Z的房间 解题报告
[bzoj4031][HEOI2015]小Z的房间 Description 你突然有了一个大房子,房子里面有一些房间.事实上,你的房子可以看做是一个包含\(n*m\)个格子的格状矩形,每个格子是一个房 ...
- tomcat和servlet的基本了解
看不清的可以下载到本地观看
- bzoj 4199 && NOI 2015 品酒大会
一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainbow 调制了 ...
- 1: @override
@Override是Java5的元数据,自动加上去的一个标志,告诉你说下面这个方法是从父类/接口 继承过来的,需要你重写一次,这样就可以方便你阅读,也不怕会忘记 @Override是伪代码,表示重写( ...
