manacher


算法原理

\(manacher\) 是根据前面已经求出的回文字符串的回文对称性质,而缩减当前位置求回文长度的时间。

\(j\) 因为 \(p_0\) 的回文性质,就可以通过 \(i\) 位置已经求好的回文长度,节省蓝色线段长度的时间。

例题

Luogu P3805 【模板】manacher 算法

因为有奇数长度的回文串,也有偶数长度的回文串,所以在相邻的字符中加入符号。

p[i]->当前位置的最大回文长度(不包括符号)
#include
#define maxn 100100001
using namespace std;
int p[maxn],pos,maxr;
char s[maxn],ss[maxn];
int ans=-1;
int n;
int main()
{
    ios::sync_with_stdio(false);
    cin>>s+1;
    int n=strlen(s+1);
    for(int i=1;i<=n;i++) ss[i<<1]=s[i],ss[(i<<1)|1]='#';
    ss[0]='@';ss[1]='#';
    strcpy(s,ss);
    n=(n<<1)|1;
    for(int i=1;i<=n;i++)
    {
        if(imaxr) maxr=i+p[i],pos=i;
        ans=max(ans,p[i]);
     } 
     cout<

Luogu P4555 [国家集训队]最长双回文串

\(\quad\)这个题目就是在每对相邻的字符中间插入符号,以此为断点,求左边最大回文子串,右边最大回文子串,答案就是每一个端点的左右相加。
\(\quad\)注意:求出后要再次递推更新求解,因为之前只更新了当前的最大值,并不一定就是全局最优解。

#include
#define maxn 51110010
using namespace std;
char s[maxn],ss[maxn];
int p[maxn],maxr,pos,l[maxn],r[maxn];
int main()
{
	ios::sync_with_stdio(false);
	cin>>s+1;
	int n=strlen(s+1);
	for(int i=1;i<=n;i++) ss[(i<<1)]=s[i],ss[(i<<1)|1]='#';
	ss[0]='@';
	ss[1]='#';
	strcpy(s,ss);
	n=(n<<1)|1;
	for(int i=1;i<=n;i++)
	{
		if(imaxr) maxr=i+p[i],pos=i;
		l[i-p[i]]=max(l[i-p[i]],p[i]);
		r[i+p[i]]=max(r[i+p[i]],p[i]); 
	}
	int ans=0;
	for(int i=3;i<=n;i+=2) l[i]=max(l[i],l[i-2]-2);
	for(int i=n;i>=1;i-=2) r[i]=max(r[i],r[i+2]-2);
	for(int i=1;i<=n;i+=2)
	{
		if(r[i] && l[i])
		ans=max(r[i]+l[i],ans);
	}
	cout<

回文条件改变的回文串问题 Luogu P3501 [POI2010]ANT-Antisymmetry

\(\quad\)首先回文串一定要是偶数,其次对称条件改为两个字符相反即可。

#include
#define ll long long
#define maxn 10000100
using namespace std;
int n,p[maxn],maxr,pos;
char s[maxn],ss[maxn];
int main()
{
	cin>>n>>s+1;
	for(int i=1;i<=n;i++) ss[(i<<1)]=s[i],ss[(i<<1)|1]='#';
	ss[0]='@';
	ss[1]='#';
	strcpy(s,ss);
	n=(n<<1)|1;
	for(int i=1;i<=n;i+=2)
	{
		if(i0 && i+p[i]+1maxr) maxr=i+p[i],pos=i;
	}
	ll ans=0;
	for(int i=1;i<=n;i+=2)
	{
		ans+=(p[i]/2);
	}
	cout<

相关