马拉车算法总结

省流:可以快速找出回文串,复杂度 \(O(n)\)

先定义一个数组P,\(P_i\) 意为以 \(i\) 为中心的最长回文串的半径。

例如ababa,P={1,2,3,2,1}

显然,我们为了处理边界和奇偶问题,可以在字符串之间加一点特殊字符。

例如,ababa变为!a#b#a#b#a?

为了求出回文,比较暴力的办法是枚举中点向外扩展。

为了快速求出P,我们引出一个性质。

无标题

不难看出,因为P[4]=3,所以a[2~6]为回文串。(红色部分)。

由于回文串的对称性,我们可以得到P[5]是大于等于他的对称点P[3]的值的。(黄色部分)。

然后再暴力扩展就好。

具体的,我们定义之前找到的回文串能走到的最右边的端点为R。

如果i<=R,则可以对称。

否则,就只能暴力扩展。

而对称点的公式为mid*2-i,推导为\((l+i)/2=mid,l+i=mid*2,l=mid*2\)

但是,这种性质也不一定成立。

例如以下情况:

无标题

显然,P[6]<p[4],原因是因为p[4]的回文串已经超过了大回文串的范围,以外的部分不能保证相等。

具体的,最大的有效范围只有R-i+1

所以P[i]=min(P[mid*2-i],R-i+1),完美!

答案为ans-1

#include<bits/stdc++.h>
using namespace std;
#define ll long long
char s,c[30000000];
ll n,mw,p[30000000],ans,mid;
int main(){
	c[0]='~';
	c[1]='|';
	while((s=getchar())!=EOF&&s!='\n') {
		n++;
		c[n*2]=s;
	}
	for(int i=1;i<n;i++){
		c[i*2+1]='|';
	}
	c[n*2+1]='|'; 
	c[n*2+2]='!';
	for(int i=1;i<=n*2;i++){
		if(i<=mw){
			p[i]=min(p[mid*2-i],mw-i+1);
		}
		else{
			p[i]=1;
		}
		while(c[i-p[i]]==c[i+p[i]]){
			p[i]++;
		}
		if(i+p[i]-1>=mw){
			mw=i+p[i]-1,mid=i;
		}
		ans=max(ans,p[i]);
	}
	cout<<ans-1;
}
//a#a#a
posted @ 2025-07-06 21:23  MistyPost  阅读(9)  评论(0)    收藏  举报