Manacher学习笔记

发布时间 2023-10-08 20:47:35作者: Amy28

1.介绍:

manacher算法用于求解回文子串问题,可以求出以一个串中每一点为中心的最长回文半径,相当于可以求出所有回文子串

2.引入:

假如要求出一个串所有长度为奇数的回文子串,暴力怎么做?
枚举以每个点为回文中心,向两侧扩展,分别比较a[p+i]与a[p-i]
时间复杂度 O(n^2)


我们考虑优化,我们可以利用已经求解出的回文子串的信息

  • 举例:a a b c b a b c

考虑回文中心在i之前的、最右端最远的一个回文子串

可以看出,在求解第7位b的回文半径时,我们是可以利用到第3位b的数据的

  • 当然,有的时候需要注意一下

此时就要注意,不能直接利用3,而是要与最远的那个回文子串进行一个比较

因此,我们只需要记下来当前右端最远的回文子串,就可以先利用之前的信息跳过一部分试验

当然,在利用过后,还需要继续用暴力的方法拓展

对了,现在只求出了所有长度为奇数的回文子串,那么长度为偶数的怎么办?

在每两个字符之间插入特殊字符即可

例如aabbaa -> #a#a#b#b#a#a#

Code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 11000010
int cnt; 
int len;
char s[maxn],s_[maxn*2];
int f[maxn*2];
void make()
{
	cnt=2;
	s_[0]='^';
	s_[1]='$';
	for(int i=0;i<len;i++)
	{
		s_[cnt++]=s[i];
		s_[cnt++]='$';
	}
	s[cnt]='&';
}//世界第一甜
void Manacher()
{
	int mid=0,ans=0,ma=0;
	for(int i=1;i<=(len*2+1);i++)
	{
		if(ma>i)
		{
			f[i]=min(ma-i,f[mid*2-i]);
		}
		else
		{
			f[i]=1;
		}
		while(s_[i-f[i]]==s_[i+f[i]])
		{
			f[i]++;
		}
		if(ma<(i+f[i]))
		{
			ma=i+f[i]; 
			mid=i;
		}
		ans=max(ans,f[i]);
	}
	printf("%d\n",ans-1);
	return;
}//华锐李泽言
int main()
{
	scanf("%s",s);
    len=strlen(s);
	make();
	Manacher();
	return 0;
}