最长回文子串之马拉车算法

最长回文子串,leetcode第5题。
最简单的做法就是暴力破解,遍历每一个可能的字符做中心,查看可能的备选回文子串,但是时间复杂度为O(n^2)。

利用动态规划,可以省去比较的过程,但是时间复杂度没有量级提升。用动态规划时踩了坑:在用动态规划解决这个问题的时候,笔者没忽略了一个细节:当子串长度小于等于3的时候,只要当前比较字符相同则不用比较内部是否相同。这个细节的忽略导致常见的二八定律,20%搞定了80%的问题,剩下越到中心的节点则分了好几种情况讨论,写了80%的代码。现在想想着实惭愧。。。。

马拉车算法(Manacher’s Algorithm)是由Manacher 1972年提出,不得不佩服这个人,因为这个算法思想其实普通大众能很直观的感受到,但是把它用详细的步骤描述出来确实厉害。算法利用已得到回文来计算以后续字符串为中心的回文。
以下为具体步骤:

第一步用一个小trick,用#间隔开原始字符串的各个字符,这样就不用考虑原始字符串长度的奇偶问题,经过改进的字符串长度为奇数。
那么原始的字符串为:abcbcaad,加入#后改进为:#a#b#c#b#c#a#a#d#,接着做以下标记
i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
arr[i] # a # b # c # b # c # a # a # d #
r[i] 1 2 1 2 1 4 1 2 1 2 1 2 3 2 1 2 1

第二步利用以上信息获取原始回文长度和起始位置
1.回文长度为r[i]-1。
为什么是r[i] -1? 假设回文长度为L, 那么加入trick后的会问总长度为2L+1, 而r[i]=L+1,所以L=r[i] - 1
2.回文的起始位置为(i-L)/2 ,注意此处不能是i/2-L/2,反例为源字符串是“a”

第三步如何计算p[i]
这个是算法的核心,比如当知道i=5时,r[1]=5. 那么r[i+1] = r[i-1],记j=i-1,max_right=i+r[i]这个有个前提就是j<max_right && (i-1)-r[i-1] > i - r[i].所以接下来的重点就是如何初始化r[j]。 在下面的代码中新的构造的字符串的开头和结尾加了^%,主要是为了防止下标越界

具体代码:

class Solution {
public:
    string longestPalindrome(string s) {
        string new_s = "^#";  // 预处理之后的string
        for(int i=0; i<s.size(); i++){
            new_s += s[i];
            new_s += "#";
        }
        new_s += "%";
        int new_length = new_s.size();
        int radius[new_length];
        int center = 1;
        int max_right = 1;
        int max_length = -1;
        int max_center = 1;  // 回文长度最长时,对应的中心
        // 计算new_s的每个字符的半径radius
        for(int i=1; i<new_length; i++){
            radius[i] = max_right > i? min(radius[2*center - i], max_right-i): 1;  
            while(new_s[i-radius[i]] == new_s[i+radius[i]]){ //如果new_s不加^%会下标地址越界
                radius[i] ++;
            }
            if(radius[i]+i > max_right){
                max_right = radius[i]+i;
                center = i;
            }
            if(radius[i]-1 > max_length){
                max_length = radius[i]-1;
                max_center = i;
            }
        }
        int start = (max_center - max_length)/2;
        string ret_s = s.substr(start, max_length);
        return ret_s;
    }
};

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值