最长回文子串,Manacher算法

烈酒焚心 提交于 2019-12-04 20:16:31

回文串就是一个字符串从左到右和从右到左是一样的,或者说他是关于中心对称的。

如何找出一个字符串中最长的回文子串呢(假设只有唯一的最长回文子串)?Manacher算法给出了O(n),的解法。

字符串的个数,偶数与奇数时判断有不同,为了简化,给每一个字符加一个特定的字符,这样就可以保证字符串始终是基数,如:#a#, #a#a#

其次需要一个idx,一个max和一个数组p,mx记录了当前所有回文串中最右边的位置,id就是这个回文串的中心位置,p[i]记录了一str[i]为中心的回文串的半边长。如下所示:

        #  a  #  b  #  c  #  c  #  d  #

p[i]   1  2  1  2  1  2  3  2  1  2  1     当访问到b的时候,mx=4, id=3。求得p,也就求得了结果。

在求p[i]时,p[i]的初始化有两种情况:  结合参考图来理解

  1. mx <= i,p[i] = 1;
  2. mx > i, p[i] = min(p[2*id - i], mx - i + 1)


上面我们讲的是p[i]的初始化,而不是最终的结果,j是i以id为中心的对称点,如果p[j]是在mx - i范围内的,取p[j],否则去mx-i,否则=mx-i+1.
之后在以i为中心,p[i]为边长想两端扩展,判断回文。在判断的时候需要注意不要越界,因为有结束符‘\0'存在,所以只需考虑起点就行。

string Manacher(string s) {
        
        string s2 = "#";
        for(int i = 0; i < s.size(); i++)    //变化字符串
            s2 = s2 + s[i] + "#";
            
        int mx = 0, mid = 0;
        int *p = new int[s2.size()];
        
        for(int i = 0; i < s2.size(); i++){
            p[i] = mx > i ? (p[2 * mid - i] > mx - i+1 ? mx - i+1 : p[2 * mid - i]) : 1;
            while(i - p[i] >= 0 && s2[p[i] + i] == s2[i - p[i]])  //这里继续完善p[i]
                p[i]++;
            
            if(p[i] + i - 1 > mx){    //定位mx
                mx = p[i] + i - 1;
                mid = i;
            }
        }
        int max = 0, index = 1;
        for(int i = 0; i < s2.size(); i++){     //找出最长的子串,并记录id
            max = (p[i] > max) ? p[index = i] : max;
        }
        delete[] p;
        
        return s.substr((index - max + 1) / 2, max - 1);    //截取最长回文子串
    }


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!