[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 ...
随机推荐
- Linux安装gitlab
一.GitLab 是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的web服务.安装方法是参考GitLab在GitHub上的Wiki页面. 二.我相信使用过git的开 ...
- 建表/修改表名/增加删除字段(MySql)
修改表名:alter table 旧表名 rename 新表名; 删除字段:alter table 表名 drop 字段名; 增加字段:alter table 表名 add 字段名 字段类型 [def ...
- 基于Spring的最简单的定时任务实现与配置(三)--番外篇 cron表达式的相关内容
本来这篇文章是会跟本系列的前两篇文章一起发布的.但是,昨天在找资料总结的时候遇到了一点意外,就延后了一些. 本篇的内容主要参考了 这篇博文:http://www.cnblogs.com/junrong ...
- 关于js回调方法 js递归时使用方法
js中递归调用本身可以这样: function a1(n){ a1(n)}但是如果需要在参数n进行自增的情况下判断会出错: function a1(n){ if(n>10) return 'aa ...
- Selenium2+python自动化-CSS定位语法
前言 一些人在使用selenium定位元素时,用的是xpath定位,因为xpath基本能解决定位的需求.css定位往往被忽略掉了,其实css定位也有它的价值,css定位更快,语法更简洁.这一篇css的 ...
- header field token is not allowed by Access-Control-Allow-Headers in preflight response问题解决
今天下午,本来打算使用aioxs在header里传一个token给后台服务器,如下图所示: 结果,控制台报了如下的错: 然后,我不停地百度,不停的改后台express的header设置,一直没有效果: ...
- POWERDESIGNER生成的代码有引号
昨天在用powerdesigner画的一个导入ORACLE中.发现都带了双引号, 当时没在意,以为是分隔符.那想后要在ORACLE查询表是一定要输入双引号才能查询.. 后来才知道而这在oracle 中 ...
- How to Manage Amazon-Fulfilled Orders - Cancel an Amazon-Fulfilled Order
You may request to cancel customer orders that have a status of "Pending" or "Unshipp ...
- 阿里与ShopRunner达成协议 联手在国内推出服务
阿里巴巴集团与美国在线零售商 ShopRunner 达成协议,将帮助后者在中国大陆销售商品和履行订单交付产品. ShopRunner 首席战略官菲奥娜·迪亚斯(Fiona Dias)周三接受媒体采访时 ...
- linux 性能分析命令及其解释
很多时候,我们需要对linux上运行的环境大体有一个了解,那么久需要大体知道当前系统的相关资源的使用情况,那么可以用一些linux提供的丰富的命令来查看 性能分析 vmstat 虚拟内存统计 用法 U ...