[APIO2014]回文串 manacher 后缀数组
题面:洛谷
题解:
还是这个性质:对于每个串而言,本质不同的回文串最多只有O(n)个。
所以我们先求出这O(n)个本质不同的回文串,然后对整个串求一次sa。
然后对于每个回文串,求出它的出现次数,更新答案即可。
如何用后缀数组求一个串的出现次数?
因为每个串都必然是某个后缀的前缀。因此我们先找到这个串x,然后在周围二分,寻找一个最大的区间[l, r]使得区间内每个串与x的LCP都大于等于这个串的长度。
可以证明,这样的区间必然是连续的一个。
因此在周围分别向上向下二分一下最多能延伸到哪,是否可行用st表维护一下height数组的最小值即可O(1)查询LCP大小。
// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 601000
#define ac 601000
#define LL long long int n, m = , pos, maxn;
LL ans;
int r[ac], sa[AC], rk[AC], p1[AC], p2[AC], b[AC], d[AC];//第几是谁,你是第几, 两个关键字,临时数组,桶
int st[AC][], h[AC], last[AC], length[AC];
char ss[AC], s[ac];//原串,扩充串 inline void upmax(LL &a, LL b){
if(b > a) a = b;
} void pre()
{
scanf("%s", ss + ), n = strlen(ss + );
s[] = '$', s[] = '#';
for(R i = ; i <= n; i ++) s[i << ] = ss[i], s[(i << ) + ] = '#';//manacher数组
for(R i = ; i <= n; i ++) sa[i] = i, rk[i] = ss[i];//初始化后缀数组
} void ssort()
{
for(R i = ; i <= n; i ++) ++ d[p2[i]];
for(R i = ; i <= m; i ++) d[i] += d[i - ];
for(R i = ; i <= n; i ++) b[d[p2[i]] --] = i;//给i分配排名(临时sa数组)
for(R i = ; i <= m; i ++) d[i] = ; for(R i = ; i <= n; i ++) ++ d[p1[i]];
for(R i = ; i <= m; i ++) d[i] += d[i - ];
for(R i = n; i; i --) sa[d[p1[b[i]]] --] = b[i];//依次给b[i]分配排名
for(R i = ; i <= m; i ++) d[i] = ;
} void sa_sort()
{
for(R k = ; k <= n; k <<= )
{
for(R i = ; i <= n; i ++)
p1[i] = rk[i], p2[i] = rk[i + k];
ssort();
int tmp = ;
rk[sa[]] = ;
for(R i = ; i <= n; i ++)
rk[sa[i]] = (p1[sa[i]] == p1[sa[i - ]] && p2[sa[i]] == p2[sa[i - ]]) ? tmp : ++ tmp;
if(tmp >= n) break;
m = tmp;
}
} void manacher()//获取回文串
{
int b = * n;
for(R i = ; i <= b; i ++)
{
r[i] = (maxn > i) ? min(r[(pos << ) - i], maxn - i + ) : ;
while(s[i - r[i]] == s[i + r[i]]) ++ r[i];
int t = i + r[i] - ;
if(t > maxn)
{
for(R j = maxn + ; j <= t; ++ j)
{
last[j] = (i << ) - j;//串的开始是要算的,,,,
if(s[j] == '#') continue;
length[j] = ((j - last[j]) >> ) + ;
}
pos = i, maxn = t;
}
} /*for(R i = 1; i <= b; i ++)
{
if(s[i] == '#') continue;
length[i] = ((i - last[i]) >> 1) + 1;//(r - l) / 2 + 1算出字母的长度(个数)
}*/
} void build()//求height数组
{
//h[sa[1]] = 0;
for(R i = , k = ; i <= n; i ++)//按照原串顺序求h
{
if(k) -- k;//将k变为上一次的h[i] - 1
int j = sa[rk[i] - ];
while(ss[i + k] == ss[j + k] && k <= n) ++ k;
h[rk[i]] = k;
}
} int t1[AC], t2[AC];//最接近i的2的n次幂的指数,对应的大小 void get_st()
{
int tt = , tmp = ;
for(R i = ; i <= n; i ++)
{
st[i][] = h[i];
if(i == (tmp << )) tmp <<= , ++ tt;
t1[i] = tt, t2[i] = tmp;
}
tmp = ;
for(R i = ; i <= ; i ++)
{
for(R j = ; j <= n; j ++)
st[j][i] = min(st[j][i - ], st[j + tmp][i - ]);
tmp <<= ;
}
} bool check(int l, int r, int lim)//看min[l, r]是否>= lim
{
int rnt = , len = (r - l + );
rnt = min(st[l][t1[len]], st[r - t2[len] + ][t1[len]]);
return rnt >= lim;
} LL half(int x, int len)//获取出现次数
{
int l = , r = x;
while(l < r)//查找前一段里面第一个符合的
{
int mid = (l + r) >> ;
if(check(mid + , x, len)) r = mid;
else l = mid + ;
}
int ll = l;//记录左边界
l = x, r = n;
while(l < r)//查找后一段里面最后一个符合的
{
int mid = (l + r + ) >> ;
if(check(x + , mid, len)) l = mid;
else r = mid - ;
}
return r - ll + ;
} void work()
{
int b = * n;
for(R i = ; i <= b; i ++)
{
if(s[i] == '#') continue;
upmax(ans, 1LL * length[i] * half(rk[last[i] >> ], length[i]));
}
printf("%lld\n", ans);
} int main()
{
// freopen("in.in", "r", stdin);
pre();
sa_sort();
build();
get_st();
manacher();
work();
// fclose(stdin);
return ;
}
[APIO2014]回文串 manacher 后缀数组的更多相关文章
- [bzoj3676][Apio2014]回文串——Manacher+后缀自动机+倍增
Brief Description 一个回文串的value定义为这个回文串的长度乘以出现次数.给定一个字符串,求\(value_{max}\). Algorithm Design 我们使用Manach ...
- [BZOJ3676][APIO2014]回文串(Manacher+SAM)
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 3097 Solved: 1408[Submit][Statu ...
- [Bzoj3676][Apio2014]回文串(后缀自动机)(parent树)(倍增)
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 3396 Solved: 1568[Submit][Statu ...
- BZOJ4755 [JSOI2016]扭动的回文串 【后缀数组】【manacher】
题目分析: 我写了史上最丑的后缀数组,怎么办? 首先manacher一遍两个串,这样只用考虑第三问.用$作为间隔符拼接两个串,把第一个串翻转.枚举回文中心,取最长的回文串,对于剩下的部分利用LCP匹配 ...
- BZOJ3676 APIO2014 回文串 Manacher、SA
传送门 首先一个结论:串\(S\)中本质不同的回文串个数最多有\(|S|\)个 证明考虑以点\(i\)结尾的所有回文串,假设为\(S[l_1,i],S[l_2,i],...,S[l_k,i]\),其中 ...
- 2018.12.15 bzoj3676: [Apio2014]回文串(后缀自动机)
传送门 对原串建立一个后缀自动机,然后用反串在上面匹配. 如果当前匹配的区间[l,r][l,r][l,r]包裹了当前状态的endposendposendpos中的最大值,那么[l,maxpos][l, ...
- bzoj 3676: [Apio2014]回文串【后缀自动机+manacher】
用manacher找出本质不同的回文子串放在SAM上跑 #include<iostream> #include<cstdio> #include<cstring> ...
- 【BZOJ 3676】 3676: [Apio2014]回文串 (SAM+Manacher+倍增)
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 2343 Solved: 1031 Description 考 ...
- BZOJ 3676: [Apio2014]回文串
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 2013 Solved: 863[Submit][Status ...
随机推荐
- HIS系统患者实体OO设计的一点思考
软件开发的生命周期中,数据库建模后,在某个数据库系统中形成相对应的表,之后再根据数据库模型设计相关的业务对象及其关系.这其实是进行了两次设计,一次是数据库模型设计,数据库模型设计是根据现实业务提取出来 ...
- WebAPI学习笔记
WebAPI WebApi是添加到Asp.Net平台的一个新特性,可以快速的创建Web服务,并对客户端提供HTTP的API调用接口 WebApi是建立在MVC框架基础之上,但不属于MVC的一部分. 序 ...
- html学习第一天
由于之后想做个网站,所以web前端的也要学习一下. 昨天看了一下html,今天做一下记录. 首先是安装工具,用文本编辑器有点麻烦,我选择的是强大的 Dreamweaver CS6,不过大家喜欢文本编辑 ...
- Maven学习(十)-----使用Maven创建Java项目
所需要的工具: Maven 3.3.3 Eclipse 4.2 JDK 8 注意:请确保 Maven 是正确安装和配置(在Windows,*nix,Mac OSX系统中),然后再开始本教程,避免 mv ...
- 一、EnterpriseFrameWork框架总体介绍
EnterpriseFrameWork框架是自己在工作之余的得意之作,经过了几年时间的不断重构,现在终于有了现在的样子:刚开始只是为了方便开发WEB系统,随着项目越做越多,新的功能也就不断补充进去,补 ...
- Elastic-Job 分布式调度平台
概述 referred:http://elasticjob.io/docs/elastic-job-lite/00-overview Elastic-Job是一个分布式调度解决方案,由两个相互独立的子 ...
- nodejs的路径问题
最近公司的一个开发项目,后端用的是nodejs.这两天需要打包给客户演示,就让公司一个小伙把之前3D机房的打包工具移植过来.打包之后,发现原本在开发环境下的跑的好好的项目,不能访问了.出现项目的首页不 ...
- JY播放器【喜马拉雅FM电脑端,附带下载功能】
今天给大家带来一款神器----JY播放器.可以不用打开网页就在电脑端听喜马拉雅FM的节目,而且可以直接下载,对于我这种强迫症患者来说真的是神器.我是真的不喜欢电脑任务栏上面密密麻麻的. 目前已经支持平 ...
- python-map, reduce, filter, lambda
目录 lambda表达式 reduce()函数 map()函数 filter()函数 tips:以下使用到的迭代器,可迭代对象,生成器等概念可以参见我的另一篇博客 lambda表达式 主要用于一行写完 ...
- 如何选择 .NET Framework目标版本
如何选择 .NET Framework目标版本 简介 .NET Framework是所有 .NET程序赖以运行的基础. 版本 到目前位置 .NET Framework共出了: .NET Framewo ...