Manacher算法
又名马拉车算法,可以在事件复杂度O(n)的情况下求解一个字符串每个回文中心的最长回文半径
暴力匹配:
从原字符串的收不开始,一次向尾部进行遍历,没访问一个字符,就以此字符为中心向两边扩展,记录改点的最长回文长度。
弊端:
1:不适合偶数回文串:abba
2:时间复杂度比较高O(n^2)
Manncher算法本质上也是基于暴力匹配的方法,不过做了一点简单的预处理,且在扩展时提供了加速。
预处理:在字符串每个字符两边加了’#’使字符串变成了奇数个字符串,长度变为了2n+1.预处理后的最长回文字符串的长度整数除以2就得到了原先字符串的最长回文串的长度。
当以’#’为中心遍历就解决了偶数回文串的问题。
回文半径数组radius是用来记录以每个位置的字符为回文中心求出的回文半径长度。所以#a#c#b#b#c#b#d#s#,所以radius数组为[1,2,1,2,1,2,5,2,1,4,1,2,1,2,1,2,1]
一个位置最右回文右边界指的是这个位置及之前的位置的回文子串,所到达的最右边的地方。比如对于字符串#a#c#b#b#c#b#d#s#
其实就是回文串最右边的下标。
最开始的时候R=-1.到p=0的位置,回文就是其本身,最右会问边界R=0;p=1时,有回文串#a#,R=2;p=2时,R=2;P=3时,R=6;p=4时,最右回文边界还是p=3时的右边界,R=6;依此类推。
最右回文右边界的对称中心C就是p=4时,R=6,C=3
算法流程
分为两种情况
情况一:下一个要移动的位置在最右回文右边界 R的右边
比如在最开始的时候R=-1,p的下一个移动的位置为p=0,p=0在R=-1的右边;p=0时,此时R=0,p的下一个移动位置为p=1,也在R=0的右边
在这种情况下,采用普遍的解法,将移动的位置设为对称中心,向两边暴力扩展,同时更新回文半径数组、最右回文右边界R和最右回文右边界的对称中心
情况二:下一个要移动的位置就是最右回文右边界R或是在R的左边。又可分为3种情况
1)cL<pL。p2是p1以c为对称中心的对称点; pL是以p2为对称中心中心的回文子串的左边界;cL是 以C为对称中心的回文子串的左边界。
这种情况下,p1的回文半径就是p2的回文半径radius[p2]
2)下cL>pL
这种情况下,p1的回文半径就是p1到R的距离,R-p1+1
3)cL=pL.这种情况下p1的回文半径就换药继续往外扩,但是只需要从R之后往外扩就可以了,扩了之后 更新R和C
整个过程的复杂度为O(n)
预处理
i
int Init(){ int len=strlen(s); s_new[0]='$';//类似于设置左边界 s_new[1]='#'; int j=2; for(int i=0;i<len;i++){ s_new[j++]=s[i]; s_new[j++]='#'; } s_new[j]='\0';//这个一定不能忘 return j;//返回s_new的长度 }
处理函数
int Manacher(){ int len=Init();//取得新字符串长度并完成向s_new的转换 int max_len=-1;//最长回文长度 int id;//C 左右回文右边界的对称中心 int mx=0;//最右回文右边界 for(int i=1;i<len;i++){ if(i<mx) p[i]=min(p[2*id-i]); else p[i]=1; while(s_new[i-p[i]]==s_new[i+p[i]]) p[i]++; if(mx<i+p[i]) { id=i; max=i+p[i]; } max_len=max(max_len,p[i]-1); } return max_len; }
作者:孙建钊
出处:http://www.cnblogs.com/sunjianzhao/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。