Manacher学习笔记
1. 用途
给定一个串,求该串中最长回文子串的长度
2. 算法过程
2.1. 预处理
回文串分为两类,奇回文和偶回文,所以对称中点是不确定的,可能是字符也可能是两个字符之间的空隙
所以可以在任意两个字符和开头结尾加上同一个奇怪的字符,此时的回文中心就一定是一个字符
2.2. 算法主体
定义数组p[i]表示以i为回文中心的最长回文半径,不难发现,p[i]-1就是最长的长度,因为要除去一个多余的字符
显然,可以向外扩展,暴力去跑有多少点符合条件,时间复杂度\(O(n^2)\)
但是在这种情况下,很多子串被重复访问,导致很多无意义的运算,时间复杂度很高
考虑优化,定义mr表示经过的,最靠右的点,而mid表示这个点是由哪个对称中心转移的
显然,对于每一个i,其实并没有必要都去拓展,可以分为如下情况:
- \(i<mr\)
因为\([mid-p_{mid},mr]\)是回文的,记i关于mid的对称点为j
根据对称的性质,显然存在,\([j-p_j,j+p_j]=[i-p_i,i+p_i]\)
但是,当\(i+p_i\)大于\(mr\)时,就不一定满足条件了,所以可以在\(i+p_j\)和\(mr\)中去一个min值,再暴力扩展
- \(i\ge mr\)
因为已经不存在对称,所以直接暴力即可
因为mid和mr都是不断右移,所以时间复杂度线性
2.3. 例题
https://gxyzoj.com/d/gxyznoi/p/112
题目描述
给出一个只由小写英文字符 \(\texttt a,\texttt b,\texttt c,\ldots\texttt y,\texttt z\) 组成的字符串 \(S\) ,求 \(S\) 中最长回文串的长度 。
字符串长度为 \(n\)。
输入格式
一行小写英文字符 \(\texttt a,\texttt b,\texttt c,\cdots,\texttt y,\texttt z\) 组成的字符串 \(S\)。
输出格式
一个整数表示答案。
样例 #1
样例输入 #1
aaa
样例输出 #1
3
提示
\(1\le n\le 1.1\times 10^7\)。
2.4. 代码
#include<cstdio>
#include<string>
#include<algorithm>
#include<iostream>
using namespace std;
int n,p[30000005];
string s,t;
int manacher()
{
int mid=1,mr=1,ans=-1;
for(int i=1;i<n;i++)
{
if(i<mr)
{
p[i]=min(mr-i,p[mid*2-i]);
}
else p[i]=1;
while(s[i-p[i]]==s[i+p[i]])
{
p[i]++;
}
if(mr<i+p[i])
{
mid=i;
mr=i+p[i];
}
ans=max(ans,p[i]-1);
}
return ans;
}
int main()
{
cin>>t;
n=t.size();
s="^|";
for(int i=0;i<n;i++)
{
s+=t[i];
s+="|";
}
s+="&";
n=s.size()-1;
printf("%d",manacher());
return 0;
}
3. 例题
3.1. [Poi2010] Antisymmetry
https://gxyzoj.com/d/gxyznoi/p/P113
题目中要求的是在取反后对称,所以它的长度必然是偶数,而且在原串中的前一半与后一半的取反结果对称
所以,显然可以用manacher,注意,在判相等时,一边用原串,一边用反串
对于一个对称中心,如果它最长的对称串长度为x,则关于该位置对称的不同的串的数量为\(\dfrac{x}{2}\)
所以相加即可,注意开long long
3.2. [bzoj3790] 神奇项链
https://gxyzoj.com/d/gxyznoi/p/P114
它就是将一些回文串拼起来,问最少需要多少个,可重叠
因为是最小值,所以一个串包含另一个串时,第二个串直接不考虑,所以对于每个对称点,只取最长的
此时可以使用一个贪心的思路,将所有串按照左右端点排序,然后从左往右找目前可以接上的串中右端点最靠后的
当无法继续向右时,就将当前串拼接然后重复此过程即可
3.3. [国家集训队] 拉拉队排练
https://gxyzoj.com/d/gxyznoi/p/P115
看到回文串,显然manacher
对于前k名乘积的问题,可以考虑后缀和,因为当以x为中心的一个回文串长度为i时,则必然存在一个长度为i-2的回文串
所以可以倒序枚举长度,然后判断有没有超过k即可
3.4. [ZJOI2009] 对称的正方形
所说的对称正方形,其实就是每一行,每一列均为回文串
显然manacher,但是这里涉及到了二维,所以可以先拆开,分别求出每个点行列的对称长度
因为折的地方可以是两行或两列的中间,所以此时不能直接算实际的对称长度
接下来可以双指针跑出上下左右分别可以走多少使它由两条对称轴分开依然是正方形,取min就是它的半径
这里可以使用st表,总时间复杂度\(O(n^2 log n)\)
但是因为加入了很多多余的点,所以在计算时要去掉求出实际的半径,它的长度就是以这个点为中心的个数