[模板] Manacher(马拉车)算法
用途
求回文子串
做法
先考虑回文子串以某字符为中心的情况,即长度为奇数
推着做,记rad[i]为以i位置为中心的最大半径(包含中点)
考虑怎么求rad[i]。找之前的一个右端点最靠右的位置p,设它的中心是j
如果有i<p,那么找到i关于j的对称点2*j-i,那么一定$rad[i]>=min\{rad[2*j-i],[p-i+1]\}$
如果i>=p,那前面做的东西对我求i没什么帮助,rad[i]>=1
然后再暴力往后判断rad[i]能不能再大一点
因为这个右端点最靠右的位置一定是递增的,所以能感受出这是一个O(n)的算法
为了解决串长为偶数的问题,我们在每两个字符间(以及开头结尾?)都插个相同的特殊符号;为了避免可能的溢出,在最开始再插个别的符号
例题
1 #include<bits/stdc++.h> 2 #define CLR(a,x) memset(a,x,sizeof(a)) 3 using namespace std; 4 typedef long long ll; 5 typedef pair<int,int> pa; 6 const int maxn=2.2e7+10; 7 8 inline ll rd(){ 9 ll x=0;char c=getchar();int neg=1; 10 while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();} 11 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); 12 return x*neg; 13 } 14 15 char s[maxn]; 16 int r[maxn]; 17 18 int main(){ 19 //freopen("","r",stdin); 20 int i,j,k; 21 scanf("%s",s+1);s[0]='!'; 22 int len=strlen(s+1); 23 for(i=len;i>=0;i--){ 24 s[i<<1]=s[i],s[i<<1|1]='#'; 25 }len=len*2+1; 26 int mp=0,mi=0,ans=0; 27 for(i=1;i<=len;i++){ 28 r[i]=i<=mp?min(mp-i+1,r[mi*2-i]):1; 29 while(s[i+r[i]]==s[i-r[i]]) r[i]++; 30 if(i+r[i]-1>mp) mp=i+r[i]-1,mi=i; 31 ans=max(ans,r[i]); 32 }printf("%d\n",ans-1); 33 return 0; 34 }