[学习笔记]马拉车-Manacher

[学习笔记]马拉车-Manacher

一.概念

​ 在日常做题中,经常会遇到求回文串的问题,而马拉车算法可以在 \(O(n)\) 的时间中求出一个字符串的最大回文子串。顺带一提,这个算法是音译。然后挂一下模板链接

二.基本思想

​ 首先对于以下两个串 \(aabaa\)\(aabbaa\) 很显然它们整个都是回文的,但是如果要寻找一个所谓的“中心”,第一个串好找,第二个则说不清。然后马拉车的一个奇妙策略是插空,即在字母与字母间插入一个“隔板”,这里我用的'|',于是两个串就变为了\(|a|a|b|a|a|\)\(|a|a|b|b|a|a|\),

​ 把插进去的隔板计算在内的话,则第一个串的”中心“是'b',第二个串的”中心“是'|',这种策略使串都成了奇数长度,于是方便找中心。

​ 此时引入两个变量,\(p[i]\)表示以i为中心的最大回文串的半径,\(mid,mx\)则分别表示当前已知的最长回文串的中心与右端点。,那么对于一个扫描到的新点 i 。有多种情况。

​ case 1:它在已知的右端点内。那么相关于中心,(有点平面镜成像的感觉?也有可能是数轴?),i 有个对称点 j,由对称点公式易得(初一数学奥),\(j=2*mid-i\).那么 j 有 一个已知的 p[j],由于\(mid*2-mx——mx\)是回文串,理想情况下可以令 p[i]=p[j] 但是实际上有可能i+p[i] 延伸到了 mx 的右边,此时无法根据 mx 的回文串性质直接相等,那么限制一下使 \(p[i]=min(p[(mid<<1)-i],mx-i+1)\) 就好。当然这还没完,需要手动探测一下两边(看代码就很清楚)

​ case 2:不在右端点内,直接手动探测奥。

最后注意维护一下mx与mid就好。由于mx一直是一直增加的,i也是由1~n,所以是\(O(n)\)

三.CODE(模板)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<fstream>
using namespace std;
const int MAXN=1.1*1e7;
char ch[MAXN*2];
int mid,mx,tot,p[2*MAXN],ans;
void read(){
	char c=getchar();
	ch[0]='~';ch[(tot=1)]='|';
	while(c<'a'||c>'z')c=getchar();
	while('a'<=c&&c<='z'){
		ch[++tot]=c,ch[++tot]='|';
		c=getchar();
	}
}
int main(){
	read();
	for(int i=1;i<=tot;++i){
		if(i<=mx)p[i]=min(p[(mid<<1)-i],mx-i+1);
		while(ch[i-p[i]]==ch[i+p[i]])++p[i];
		if(i+p[i]>mx)mx=i+p[i]-1,mid=i;
		if(p[i]>ans)ans=p[i];
	}
	cout<<ans-1;
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/clockwhite/p/13381920.html