关于manacher算法,似乎在学完KMP之后,比较容易上手,虽然有些原理方面,我没有理解的太深。
Manacher就是解决回文串的问题,求一个字符串中的最长回文子串。
Manacher算法首先对字符串进行处理:在所有字符之间插入‘#’,这样的好处是,无论最长回文子串是奇数个或者是偶数个,都可以进行处理。
处理过程是这样的
假设原串是这样的
1 2 3 4 5
a b b a d
处理完成一个新数组
0 1 2 3 4 5 6 7 8 9 10 11 12
? # a # b # b # a # d # 0
1 2 1 2 5 2 1 2 1 2 1
首尾设置完全不相干的字符,是为了检测回文时,不会被算进去。
最下一列叫做P[i] ,用来记录当前位的回文个数,如果前后都不回文,默认p[i]=1,(可以当成自身回文)。
算法核心部分有三部分
还是直接用代码来讲吧:
HD#include <iostream>
#include <cstdio> #include <cstring> #define maxn 1100050 char str[maxn]; char a[maxn<<1]; int p[maxn<<1]; int min(int x,int y) { if (x<y) return x; return y; } int main() {while (scanf("%s",&str[1])) { if (str[1]=='E') break; int i,j; a[0]='?',a[1]='#'; for (i=1; str[i]; i++)//处理成新的字符串。 { a[(i<<1)]=str[i]; a[(i<<1)+1]='#'; } int n=(i<<1); a[n]=0; int maxid=0,maxl=0,id=0; for (i=1; i<n; i++)//代码的核心部分。 { if (maxid>i)//如果之前记录的最大回文覆盖超过了当前点坐标,进行比较(这一步其实我也不是很理解,只知道可以节省搜索,优化时间) p[i]=min(p[2*id-i],maxid-i); //进行覆盖度的比较,取较小的为p[i] else p[i]=1; //否则直接定义为默认值1。 while (a[i+p[i]]==a[i-p[i]]) p[i]++; //前后搜索,若回文,则+1; ifmaxid)//获取当前最大覆盖面,和覆盖点。 { maxid=p[i]+i; id=i; } if (p[i]>maxl) maxl=p[i]; //最大回文长度保存在p[i]中,取p[i]最大值即可。(此时最长回文串的中间点即为i点(不过是加了‘#’的新串)) } printf("%d ",maxl-1); } return 0; }