LeetCode Longest Palindromic Substring Part Manacher ALGORITHM

class Solution {
public:
    string preProcess(string s){//避免回文串的奇偶性讨论
        string ans="";
        if (s.size()==0) {
            return "^$";
        }
        ans.append("^");
        for (int i=0; i<s.size(); i++) {
            ans.append("#");;
            ans+=s[i];
        }
        ans.append("#$");
        return ans;
        
    }
    string longestPalindrome(string s) {
        string ans="";
        if (s.size()==0||s.size()==1) {
            return s;
        }
        string T=preProcess(s);
        int n=T.length();
        int *P=new int[n];//动态开辟数组;
        int C=0,R=0;
        int max=-1;
        int start=0;//记录最长回文串的开始位置。
        for (int i=1; i<T.size()-1; i++) {//计算P[i]
            int i_mirrior=2*C-i;//求得i关于C点对称的点的坐标
            P[i]=R>i?min(P[i_mirrior], R-i):0;//如果能被C为中心的回文串覆盖到的话,那么可以直接等于P[i_mirror]
            while (T[i-P[i]-1]==T[i+P[i]+1]) {//尝试向左右两边扩展i为中心的回文串
                P[i]++;
            }
            if (i+P[i]>R) {//如果已经超过右边界,则更新C,R。
                C=i;
                R=i+P[i];
            }
            if (P[i]>max) {
                max=P[i];
                start=i-P[i];
            }
        }
        ans=s.substr((start-1)/2,max);
        return ans;
    }
};

参考http://articles.leetcode.com/2011/11/longest-palindromic-substring-part-ii.html学习了一下Manacher算法来求解最长回文子串的问题。

manacher算法在O(n)时间内求解出最长回文子串。首先,我们要对字符串S进行预处理,目的是得到只存在奇数长度的回文子串的字符串,避免了回文串长度奇偶性的讨论。转化步骤是在S的每一个字符之间插入一个‘#’。

例如:S="abababa",T="#a#b#a#b#a#b#a#";

为了找到最长回文子串,我们以每个T[i]为中心,向两边扩展,使得T[i-d]...T[i+d]组成一个回文串。那么在T中的最长回文串长度为2d+1。仔细观察可以发现,在原字符串中的回文串长度正好是d;

例如:S="aba",T="#a#b#a#".在T中的最长回文串为"#a#b#a#",center 为b,i=3,d=3;T[3-3]...T[3+3]组成最长回文串,长度为7.正好对应原字符串中的最长回文串的长度为d,即3.

那么,如何求得T中的最长回文子串呢?

我们引入辅助数组P。P[i]表示以T[i]为中心的回文串向两边散开的长度,即上面说到的d。

例如:

(图片来自于http://articles.leetcode.com/2011/11/longest-palindromic-substring-part-ii.html)由数组P,我们能立马得到最长回文串为abaaba。

现在我们想像一下在回文串abaaba中有一条直线画在中间。可以观察到直线两边的严格对称性。现在我们考虑一种更为复杂的情况。

假设我们已经计算得到了部分数组P的值,如图所示,我们已经有了P[1]-P[12],现在欲求解P[13].其中图中的黑色实线表示以a为中心的最长回文串,两边的L,R黑色点线表示以a为中心的最长回文串的两个边界。i'表示i以c为中心对称的点,即9。我们怎么能快速得到P[13]呢?


如图所示,两条绿色的横线表示了以i'与i为中心的最长回文子串的覆盖范围,显然P[i']=1.由于i‘为中心的回文子串,完全包含以C为中心的回文串中,且完全在C左边,由于对称性,显然,以i'为中心的回文串与以i为中心的回文串是完全对称的。因此,P[i]=P[i']=1;类似的,我们可以观察到P[12]=P[10]=0,P[13]=P[9]=1,P[14]=P[8]=0;

那么,现在我们考虑一种更为复杂的情况:


图中,绿色实线部分表示以i为中心以及以i'为中心的回文串中关于C严格对称的部分。穿越过center的部分,即绿色虚线的部分,也是严格对称的。但是,我们要注意到左边红线部分,以i'为中心的回文串已经超过了C为中心的回文串的左边界(图中红色部分)。我们由对称性能够得到的结论仅仅是p[15]>=5(绿色实线部分).要求解P[15],我们只能继续比较P[21]==P[9]吗??因为并不相等,所以我们得到结论P[15]=5.

所以总结一下:

--------------------------------------------------

if(P[i']<=R-i)

P[i]=P[i']

else

由右边界开始向外一对一对比较咯。

------------------------分割线------------------------

那么问题又来了。C与R的值是怎么维护的呢?假如刚才15为中心的回文串一路跨越了右边界R。那么我们就将C改为15,R改为以15为中心的右边界。为什么?将R右移,我们在求解接下来的P[i]的时候,是不是i为中心的子串被覆盖的可能性变大了呢?

总结一下:


如果i为中心的回文串扩展超过了边界R,则更新Center 为i,右边界为新的回文串的右边界。

------------------------分割线------------------------


最后,假如我们得到了T中的最长子串。如何对应到原字符串呢?在实际的代码中,参考上述网址的代码,在预处理的过程中,在前后边界还插入了特殊字符来避免边界讨论,,即S=“aba”,T=“^#a#b#a#$”,我们最后得到的最长回文串为T.substr(start_index,maxlen*2+1);对应于S的substr是什么呢?仔细观察可以得出,每个T中的index对应于S的下标为(index-1)/2。而长度我们前面分析过了,就是P数组中存的值,这里就是maxlen,因而。为s.substr((start_index-1)/2,maxlen); 希望能对大家有所帮助。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值