月雩の小窝(损坏)
 
Made by 月雩 禁止转载(作者允许除外) | Theme: Fog
载入天数...
载入时分秒...
总访问量:  |   访问人数:
Copyright © 2020 pray-yueyu.github.io

Manacher(马拉车)

  热度: loading...

一、背景

1975年,Manacher发明了Manacher算法(马拉车算法),是一个可以在O(n)O(n)的复杂度中返回字符串s中最长回文子串长度的算法。

二、算法过程分析

1.输入转化

回文串分为奇回文偶回文,例如,ababa'ababa'中字符个数为5且为回文串,所以它是奇回文,而'abba'字符个数为4且为回文串,所以它是偶回文。

显然,奇回文与偶回文很是不一样,比较难处理,所以我们将输入的字符串转换一下,规则如下:

1.在第一个字符前添加一个不常用字符(常用'$'),以此充当边界

2.在最后一个字符后添加一个不常用字符(常用'\0'),以此充当边界

3.在第一个字符前('$'后),最后一个字符后('\0'前)和每两个字符之间添加一个不常用字符(常用'#')

这样以后,上文的'ababa'就会变成'a#b#a#b#a'(边界未标出),'abba'就会变成'a#b#b#a',它们都是奇回文,这样就会更好处理。

2.过程分析

FiF_i表示以ii为中点,回文字符串的最大半径长度。

举个例子。令字符串S='abbadcacda'

i 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
SS $ # a # b # b # a # d # c # a # c # d # a # \0
FiF_i ϕ\phi 1 2 1 2 5 2 1 2 1 2 1 2 1 8 1 2 1 2 1 2 1 ϕ\phi

显然,最终的答案就为MaxsiSFi1Max_{s_i \in S}F_i-1

那么如何求FiF_i呢?

来看一幅图~

其中,jj表示ii关于MidMid的对称点,NN表示MM关于MidMid的对称点,且MM=MidMid+FMidF_{Mid}

具体来说,MM就是以MidMid为中心的最长回文右边界,ii为当前所求编号。

如果i<Mi<M(如图),显然有FiF_i=min(FjF_j,jNj-N)

现在知道NN ~ MidMid这段与MidMid ~ MM这段关于MidMid对称,且jFjj-F_j ~ jj这段与jj~j+Fjj+F_j这段关于jj对称,那么显然有:

①当jFj>=Nj-F_j>=N时iFji-F_j ~ ii这段与ii ~ i+Fji+F_j这段关于ii对称

②当jFj<Nj-F_j<N时MidMid ~ ii这段与MidMid~MM这段关于ii对称

Fi={min(Fj,jN)=min(FMid2i,Mi)i<M1i>M综上,有F_i= \begin{cases} \color{black}min(F_j,j-N)=min(F_{Mid*2-i},M-i),i<M \\\color{black}1,i>M\\ \end{cases}

这样以后就可以在O(1)的时间复杂度内知道iFii-F_i ~ i+Fii+F_i必定为回文字符串,那么再从此向外暴力寻找,就可以得到FiF_i的最终值

若最终的Fi+i>MF_i+i>M,那么为了让后面的FF值可以更快地求出,我们需要更新MidMidMM值,代码如下:

if(F[i]+i>M)
    M=F[i]+i,Mid=i;

三、蒟蒻的代码展示

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=11000010;
char s[N*2];
int F[N*2],ans,pl;
void readn()
{
	char ch=getchar();
	s[0]='S',s[1]='#';pl=1;//注意从1开始
	while(ch<'a'||ch>'z')ch=getchar();
	while(ch>='a'&&ch<='z')s[++pl]=ch,s[++pl]='#',ch=getchar();
	s[++pl]='\0';
}
int main()
{
	readn();
	for(int i=0,M=0,Mid=0;i<=pl;i++)
	{
		if(M>i)F[i]=min(F[(Mid<<1)-i],M-i);
		else F[i]=1;
		while(s[i-F[i]]==s[i+F[i]])F[i]++;
		if(F[i]+i>M)M=F[i]+i,Mid=i;
		ans=max(ans,F[i]-1);
	}
	printf("%d\n",ans);
	return 0;
} 
//**月雩·薇嫭**