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的& ...
随机推荐
- 为phpstorm安装右侧代码预览工具
打开设置界面,进入到plugins页面,然后再搜索codeglance,然后点击安装即可.
- [百度贴吧]10GB 通信线缆
现在,即使光纤通信能够带来最低延迟的优势,但是许多IT部门依然在10G以太网(10G bE)中使用铜缆布线,来实现交换机和交换机或者和服务器之间的连接.目前主要有两种主要的铜缆布线技术应用在10 Gb ...
- 【php】new static的用法
在一个类中,常见的是new self()操作,代表返回自身类的实例. 当父类中存在方法,然后每个子类继承于父类,调用这个方法会返回自身的实例化对象, <?php class A { functi ...
- 高性能页面加载技术--BigPipe设计原理及Java简单实现
1.技术背景 动态web网站的历史可以追溯到万维网初期,相比于静态网站,动态网站提供了强大的可交互功能.经过几十年的发展,动态网站在互动性和页面显示效果上有了很大的提升,但是对于网站动态网站的整体页面 ...
- 【转】器件为什么只听英文Datasheet的话
浅谈为什么要阅读英文数据手册 ——带你Go Through Datasheet 系列 Unfortunately!从事软硬件(固件)开发的工程师都知道,我们所用的元器件,特别是高端器件和芯片,都是来自 ...
- Jump Game - LeetCode
目录 题目链接 注意点 解法 小结 题目链接 Jump Game - LeetCode 注意点 解法 解法一:贪心算法,只关注能到达最远距离,如果能到达的最远距离大于结尾说明能到达,否则不能.并且如果 ...
- loj2542 「PKUWC2018」随机游走 【树形dp + 状压dp + 数学】
题目链接 loj2542 题解 设\(f[i][S]\)表示从\(i\)节点出发,走完\(S\)集合中的点的期望步数 记\(de[i]\)为\(i\)的度数,\(E\)为边集,我们很容易写出状态转移方 ...
- Android Studio快捷键设置之实现原eclipse中ctrl+m的全屏的效果
如下图,keymap设置成eclipse的的,但是鼠标双击最大化复原没有,ctr+M全屏也没有, 那么久在如下图的三个项目上添加鼠标双击和快捷键---自己方便好用,但不冲突的 Toggle full ...
- 【agc003D】Anticube
Portal --> agc003D Description 给你\(n\)个数,要从里面选出最多的数满足这些选出来的数中任意两个数的乘积都不是立方数 Solution (为什么感觉最近这种解法 ...
- E. Turn Off The TV Educational Codeforces Round 29
http://codeforces.com/contest/863/problem/E 注意细节 #include <cstdio> #include <cstdlib> #i ...