bzoj3676 [Apio2014]回文串 SAM+树上倍增

链接

bzoj

luogu

思路

根据manacher可以知道,每次暴力扩展才有可能出现新的回文串。

所以推出本质不同的回文串个数是O(n)级别的。

每次查询一个串出现的个数。

建立出parent树,一个串出现的个数就是对应parent树上的所在的子树的大小,利用树上倍增可以log求出一个串出现的个数。

具体一点,我们知道endpos,可以找到对应parent tree的节点。

然后目标节点肯定是在根到此节点的路径上的(他是她的后缀嘛)。

用子串长度和节点的longest比较就行了,倍增慢慢跳。

总的复杂度\(O(nlogn)\)

当然还有更简单更优秀的的回文自动机。

卡常!!

脸丑的bzoj,luogu会MLE。

所以利用我你们的卡常技巧

parent树的树高不会太高,数据中应该深度都小于1000,倍增开10倍。(有点面向数据了)

sam状态是3*n,用map或unordered_map牺牲一点复杂度去省空间(测得用map毫无用处,内存增大)。

当然,可以不用建立出parent tree的图,因为你已经有了父节点表示法的图了。

老方法:基数排序endpos去更新parent tree的size

还是wxy super cool.我再也不建parent树了,vector内存太大了。

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=600007;
int read() {
int x=0,f=1;char s=getchar();
for(;s>'9'||s<'0';s=getchar()) if(s=='-') f=-1;
for(;s>='0'&&s<='9';s=getchar()) x=x*10+s-'0';
return x*f;
}
int n,len,p[N],st[N][11],c[N],a[N];
char s[N],S[N];
struct node {
int len,fa,ch[26];
}dian[N];
int siz[N],tot=1,las=1,i_can_find_it[N];
void add(int c,int k_th) {
int p=las;int np=las=++tot;
dian[np].len=dian[p].len+1;
for(;p&&!dian[p].ch[c];p=dian[p].fa) dian[p].ch[c]=np;
if(!p) dian[np].fa=1;
else {
int q=dian[p].ch[c];
if(dian[q].len==dian[p].len+1) dian[np].fa=q;
else {
int nq=++tot;dian[nq]=dian[q];
dian[nq].len=dian[p].len+1;
dian[q].fa=dian[np].fa=nq;
for(;p&&dian[p].ch[c]==q;p=dian[p].fa)
dian[p].ch[c]=nq;
}
}
siz[las]=1;
i_can_find_it[k_th]=las;
}
int query(int l,int r) {
int u=i_can_find_it[r];
for(int i=10;i>=0;--i)
if(dian[st[u][i]].len>=r-l+1) u=st[u][i];
return siz[u];
}
int main() {
// freopen("testdata.in","r",stdin);
scanf("%s",s+1);
n=strlen(s+1);
//build sam
for(int i=1;i<=n;++i) add(s[i]-'a',i);
//build parent tree
for(int i=2;i<=tot;++i) st[i][0]=dian[i].fa;
for(int i=1;i<=tot;++i) c[dian[i].len]++;
for(int i=1;i<=tot;++i) c[i]+=c[i-1];
for(int i=1;i<=tot;++i) a[c[dian[i].len]--]=i;
for(int i=tot;i>=1;--i) siz[dian[a[i]].fa]+=siz[a[i]];
//init st
for(int j=1;j<=10;++j)
for(int i=1;i<=n;++i)
st[i][j]=st[st[i][j-1]][j-1];
//manacher -- init
S[0]='@';
for(int i=1;i<=n+n;i+=2) S[i]='#',S[i+1]=s[i/2+1];
S[len=n+n+1]='#';
int id=0,mx=0;
ll ans=0;
//manacher -- do
for(int i=1;i<=len;++i) {
if(i<mx) p[i]=min(mx-i,p[id*2-i]);
else p[i]=1;
while(S[i+p[i]]==S[i-p[i]]) {
int l=(i-p[i]+1)/2,r=(i+p[i])/2;
if(l<=r) ans=max(ans,1LL*(r-l+1)*query(l,r));
p[i]++;
}
if(mx < i+p[i]-1) mx=i+p[i]-1,id=i;
}
//print
printf("%lld\n",ans);
return 0;
}

bzoj3676 [Apio2014]回文串 卡常+SAM+树上倍增的更多相关文章

  1. [模板] 回文树/回文自动机 && BZOJ3676:[Apio2014]回文串

    回文树/回文自动机 放链接: 回文树或者回文自动机,及相关例题 - F.W.Nietzsche - 博客园 状态数的线性证明 并没有看懂上面的证明,所以自己脑补了一个... 引理: 每一个回文串都是字 ...

  2. [BZOJ3676][APIO2014]回文串(Manacher+SAM)

    3676: [Apio2014]回文串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 3097  Solved: 1408[Submit][Statu ...

  3. [Bzoj3676][Apio2014]回文串(后缀自动机)(parent树)(倍增)

    3676: [Apio2014]回文串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 3396  Solved: 1568[Submit][Statu ...

  4. bzoj 3676 [Apio2014]回文串(Manacher+SAM)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3676 [题意] 给定一个字符串,定义一个串的权值为长度*出现次数,求最大权的回文子串. ...

  5. [bzoj3676][Apio2014]回文串——Manacher+后缀自动机+倍增

    Brief Description 一个回文串的value定义为这个回文串的长度乘以出现次数.给定一个字符串,求\(value_{max}\). Algorithm Design 我们使用Manach ...

  6. BZOJ3676: [Apio2014]回文串(SAM+Manacher/PAM)

    Description 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的“出 现值”为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最 大出现值. Input 输入只有一行 ...

  7. BZOJ3676 APIO2014回文串(manacher+后缀自动机)

    由于本质不同的回文子串数量是O(n)的,考虑在对于每个回文子串在第一次找到它时对其暴力统计.可以发现manacher时若右端点移动则找到了一个新回文串.注意这样会漏掉串长为1的情况,特判一下. 现在问 ...

  8. 2018.12.15 bzoj3676: [Apio2014]回文串(后缀自动机)

    传送门 对原串建立一个后缀自动机,然后用反串在上面匹配. 如果当前匹配的区间[l,r][l,r][l,r]包裹了当前状态的endposendposendpos中的最大值,那么[l,maxpos][l, ...

  9. BZOJ3676[Apio2014]回文串——回文自动机

    题目描述 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的“出 现值”为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最 大出现值. 输入 输入只有一行,为一个只包含小写字 ...

随机推荐

  1. Linux学习笔记之LVM基本应用,扩展及缩减实现

    0x00 LVM概述 LVM是逻辑盘卷管理(Logical Volume Manager)的简称,它是Linux环境下对磁盘分区进行管理的一种机制,LVM是建立在硬盘和分区之上的一个逻辑层,来提高磁盘 ...

  2. arguments 使用

    function test(){ var paramsNum = arguments.length; var sum = 0; for(var i = 0;i<paramsNum;i++){ c ...

  3. 【WPF】1、 基本控件的简介

    WPF一直都是断断续续的使用.偶尔用到一下.但是每次间隔比较长,需要重新学习,就写了这篇日志.以后有问题,看这个就可以了解各大概,然后针对细节再另外想办法. 微软的东西真心好,如果什么都不懂,可以直接 ...

  4. linux部署安装SRS流媒体服务器教程

    这段时间一直在搞RTMP流媒体直播项目,期间踩过很多坑,刚开始是用的nginx-rtmp作为流媒体转发服务器,但是效果并不尽人意,推拉流不稳定,特别是拉流,速度特别慢,平均要十多秒才能拉到流,并且交互 ...

  5. spark源码阅读--shuffle读过程源码分析

    shuffle读过程源码分析 上一篇中,我们分析了shuffle在map阶段的写过程.简单回顾一下,主要是将ShuffleMapTask计算的结果数据在内存中按照分区和key进行排序,过程中由于内存限 ...

  6. angularJS中select元素的应用浅析

    select array 数据: select ng-model 用法: 1.可以是一个对象形式,ng-model="test" $scope.test = {name: &quo ...

  7. css中absolute设置问题和如何让div居中

    今天设置多个div到页面正中间的时候,在第一层<div class="map">中设置如下: .map{ position:absolute: top:50%; lef ...

  8. Fiddler抓本机包

    使用Fiddler抓本机包的方法: File -->Capture Traffic 选中之后自动设置本机的Internet代理选项.

  9. JavaScript 数组(一)数组基础

    一.数组 1.概述 数组 就是将多个元素按一定顺序排放到一个集合中,那么这个集合称之为数组. 数组 也是一种数据类型,属于复杂数据类型(Object). 2.特点 存放的元素有序的. 可以存放不同的数 ...

  10. JavaScript 之 location 对象

    一.location 对象 location 对象是 window 对象下的一个属性,使用的时候可以省略 window 对象. 常用属性: location.href = 'http://www.ba ...