部分文章内容为公开资料查询整理,原文出处可能未标注,如有侵权,请联系我,谢谢。邮箱地址:gnivor@163.com ►►►需要气球么?请点击我吧!

算法--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();        
    }
}

 

posted @ 2015-07-14 10:43  流了个火  阅读(96)  评论(0)    收藏  举报
►►►需要气球么?请点击我吧!►►►
View My Stats