Manacher总结

FREEH

2018-07-24 22:21:57

Solution

# Manacher总结 ### 【用途】 - 求一个字符串中有多少个回文**子串**。 ### 【算法过程】 #### 1. 预处理 - 由于回文串分为偶回文串和奇回文串,奇偶判断起来比较麻烦,因此我们可以在字符串的首、尾以及各个字符之间添加一些“神奇”字符(不妨使用$),但是要注意字符串的首添加的字符**必须**区别于各个字符之间的字符。 - 不难发现,修改后的字符串都变成了奇字符串。 - **以下Manacher算法的所有处理都是基于修改后的字符串**。 #### 2. Manacher算法的主体 - 定义数组p[i]表示以字符i为回文中心的最长回文串的半径,不难发现,p[i]-1就是字符串中最长回文串的长度(因为要去除$)。 - 定义mx和mid,表示目前找到的回文串的右端的最右是mx,中心是mid。 - ![1](https://cdn.luogu.com.cn/upload/pic/25258.png) - 如上图所示,id是j~mx的回文中心,因此以i的对称点为回文中心的字符串=以i为回文中心的字符串,因此p[i]可以是p[j]。但是由于超过mx的部分(即右红边部分)**不能保证**必定等于左红边的部分,所以p[i]不能超过i~mx的长度。因此 ```cpp p[i]=min(mx-i,p[mid*2-i]) ``` - 接下来,我们就要**暴力**统计右侧 “不能保证” 的部分,即最右黑边的右侧是否满足回文性质(具体看代码)。 - 最后更新一下mx即可。 ### 【时空复杂度】 - Manacher算法的时间复杂度为$O(n)$(这里就不作证明了),因此是解决回文问题的利器。 ### 【参考程序】 ```cpp #include<cstdio> #include<cstring> #define min(a,b) (a<b?a:b) #define max(a,b) (a>b?a:b) char s[32000005],st[32000005]; int p[32000005]; int change() { int len=strlen(st); int j=2; s[0]='^'; s[1]='$'; for (int i=0;i<len;i++) { s[j++]=st[i]; s[j++]='$'; } s[j]='&'; return j; } int Manacher() { int len=change(),mid=1,mx=1,ans=-1; for (int i=1;i<len;i++) { if (i<mx) p[i]=min(mx-i,p[mid*2-i]); else p[i]=1; while (s[i-p[i]]==s[i+p[i]]) p[i]++; if (mx<i+p[i]) { mid=i; mx=i+p[i]; } ans=max(ans,p[i]-1); } return ans; } int main() { scanf("%s",st); printf("%d",Manacher()); } ```