算法--manacher算法
原文链接:http://blog.csdn.net/xingyeyongheng/article/details/9310555
这是一种求最长回文子串的方法
定义数组p[i]表示以i为中心的(包含i这个字符)回文串半径长.
将字符串s从前扫到后for(int i=0;i<strlen(s);++i)来计算p[i],则最大的p[i]就是最长回文串长度,则问题是如何去求p[i]?
由于s是从前扫到后的,所以需要计算p[i]时一定已经计算好了p[1]....p[i-1]
假设现在扫描到了i+k这个位置,现在需要计算p[i+k]。
定义maxlen是i+k位置前所有回文串中能延伸到的最右端的位置,即maxlen=p[i]+i;//p[i]+i表示最大的
分两种情况:
1. i+k这个位置不在前面的任何回文串中,即i+k>maxlen,则初始化p[i+k]=1;(本身是回文串)
然后p[i+k]左右延伸,即while(s[i+k+p[i+k]] == s[i+k-p[i+k]])++p[i+k]
2. i+k这个位置被前面以位置i为中心的回文串包含,即maxlen>i+k
这样的话p[i+k]就不是从1开始
由于回文串的性质,可知i+k这个位置关于i与i-k对称,所以p[i+k]分为以下3种情况得出
//黑色是i的回文串范围,蓝色是i-k的回文串范围,
2.1 第一种情况:
i-k回文串范围有一部分在i的范围之外,如上图蓝色左端在黑色左端之外。
这种情况p[i+k]=p[i]-k;即半径为橙色的部分。
那么p[i+k]可不可能更长呢?答案是不可能。
解释:
如上图,加入p[i+k]可以延长增加紫色d部分,那么相应的左端延长增加紫色部分c,根据i的回文串c和紫色部分b对应,再根据i-k的回文串b和紫色部分a对称,最终得到a和d对称,所以i的回文串就不是黑色部分了,而是黑色部分+左右两端紫色部分,这和已有的i的回文串矛盾,所以假设不成立。因此p[i+k]=p[i]-k; //=maxlen-(i+k)
2.2 第二种情况:
第二种情况:i-k回文串全部在i范围之内,如上图,此时p[i+k]=p[i-k]
那么p[i+k]可不可能更长?答案是不可能的
解释:
假设p[i+k]可以延长,并增加紫色部分d,则相应的增加左端紫色部分c。根据i是回文串,c和紫色部分b对称,d和紫色部分a对称,所以i-k回文串是蓝色部分+左右两端紫色部分,与前面求出的i-k回文串矛盾,所以假设不成立,则p[i+k]=p[i-k]
2.3 第三种情况:
i-k回文串左端部分与i回文串左端部分重合,即
(i-k)-p[i-k]==i-p[i]
则先让p[i+k]=p[i+k],而且p[i+k]可能继续增加,
因此需要while(sb.charAt(i+k-p[i]) == sb.charAt(i+k+p[i])) p[i+k]++;

将1、2.1、 2.2、 2.3三种情况合并
p[i+k]=min(p[i-k],p[i]-k); //k=(i+k)-i while(s[i+k+p[i+k]]==s[i+k-p[i+k]]) p[i+k]++
根据上面算法我们知道p[i]是以i为中心的回文串长度,对于aa这种偶数长度的字符串求回文串发现中心i不是一个字符,而是空的,因此我们要把偶数字符串转换成奇数字符串。因此在字符串中插入以前没有出现过的字符"#",因此长度变为2*len+1。如果原来字符为"abcbaa",那么改版后形式为"#a#b#c#b#a#a#", 再在首尾加上不同的字符,用于识别结束,整个变为:"*#a#b#c#b#a#a#^"
完整代码
public class Solution3 { public static void main(String[] args) { //String str = longestPalindrome("bb"); String str = longestPalindrome("ababccbaaaaeceaa"); //String str = longestPalindrome("abcba"); System.out.println("最长回文子串为:" + str); } public static String longestPalindrome(String s) { int len = s.length(); StringBuffer sb = new StringBuffer(); sb.append("*"); //头部加一个* sb.append("#"); for(int i = 0 ; i <len ;i++){ sb.append(s.charAt(i)); sb.append("#"); } sb.append("^"); //尾部加个^,注意要和头部不同 int sblen = sb.length()-2; int[] p = new int[sblen]; int id = 0 ; int maxlen = 0; int maxid = 0; for(int i=2;i<sblen;i++){ if(p[id]+id>i) p[i]=Math.min(p[2*id-i],p[id]+id-i); else p[i]=1; while(sb.charAt(i-p[i]) == sb.charAt(i+p[i])){ p[i]++; } if(id+p[id]<i+p[i]) id=i; if(maxlen<p[i]){ maxlen=p[i]; maxid =i; } } StringBuffer solu = new StringBuffer(); for(int i = maxid-maxlen+2 ; i <maxid+maxlen ; i+=2){ solu.append(sb.charAt(i)); } return solu.toString(); } }

浙公网安备 33010602011771号