Manacher算法+注释
Manacher算法是用来求一个字符串中最长回文串的算法。
考虑暴力求最长回文串的做法:
暴力枚举字符串中的所有字串判断是否回文,然后求最大值。
时间复杂度O(n^3),考虑优化。
我们从枚举所有字串改成枚举所有回文串的对称轴,向左右扩展直到不相等,得到最长回文串。
优化到O(n^2),还是不够优秀。
于是我们引出Manacher算法。
先向字符串s中插入特殊字符得到字符串str,这样我们就不用讨论字符串长度是奇是偶了。
用一个辅助数组p表示每个点可以扩展出去的最长回文长度
从str[1]扫到str[strlen(str)],再设置两个变量mr表示已触及的最右边的字符,mid表示包含mr的回文串的对称轴位置。
当i属于(mid,mr)时,显然i关于mid的对称点是(mid<<1)-i(中点坐标公式简单推一下),由于回文串对称串的全等性,我们令p[i]=p[(mid<<1)-i],然后接着尝试扩展:str[i+p[i]]==str[i-p[i]](前后是否对称),p[i]++
若i>mid,我们就设置mid=i,mr=当前扩展到的最右字符。
给出代码结束本篇博客
#include<bits/stdc++.h> using namespace std; inline int read(){ int data=0,w=1;char ch=0; while(ch!='-' && (ch<'0'||ch>'9'))ch=getchar(); if(ch=='-')w=-1,ch=getchar(); while(ch>='0' && ch<='9')data=data*10+ch-'0',ch=getchar(); return data*w; } const int maxn=5e7+10; int n,p[maxn],ans; char s[maxn],str[maxn]; void init(){ str[0]=str[1]='$';//ccf喜欢这个 for(int i=0;i<n;i++) str[(i<<1)+2]=s[i],str[(i<<1)+3]='$'; n=(n<<1)+2; str[n]=0; } void Manacher(){ int mr=0,mid; for(int i=1;i<n;i++){ if(i<mr) p[i]=min(p[(mid<<1)-i],p[mid]+mid-i); else p[i]=1; for(;str[i+p[i]]==str[i-p[i]];p[i]++); if(i+p[i]>mr) mr=p[i]+i,mid=i; } } int main(){ scanf("%s",s); n=strlen(s); init();Manacher(); ans=0; for(int i=0;i<n;i++) ans=max(ans,p[i]); printf("%d\n",ans-1); return 0; }
下一篇更新一些数论知识