马拉车算法总结
省流:可以快速找出回文串,复杂度 \(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
可以自由转载