Longest Palindromic Substring - LeetCode

Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.


转自: http://blog.csdn.net/pickless/article/details/9040293

另外再推荐一篇其他不错的有关这个算法的博客: http://blog.csdn.net/ggggiqnypgjg/article/details/6645824

还有一个简答的O(n * n)的算法,面试比较实用: http://blog.csdn.net/magisu/article/details/16815961


选定一个位置,朝两边不断拓展,直到两边的字符不一样为止。

这是最简单、最直观的想法,这个想法的时间复杂度是O(n2)。


但是这不是最快速的算法,目前对于最长回文子串问题最快的解法的时间复杂度为O(n)。


假设S=“gxgagxgag”,我们已经知道了以S[0]~S[3]为中心的最长回文子串。

现在要求以S[4]中心到以S[8]中心的最长回文子串。


S[3]为中心的最长回文子串为S[0]~S[6]:"gxgagxg",根据对称性,我们可以由S[2]推出S[4]的基本情况:

以S[2]为中心的最长回文子串的长度为1(S[2]本身),将这一段以S[3]为中心翻转过去,会变成S[4]本身,所以以S[4]为中心的最长回文子串的长度也为1。

证明:S[0]到S[6]是以S[3]为中心的回文串,所以S[0]==S[6],S[1]==S[5],S[2]==S[4]。又因为以S[2]为中心的最长回文子串的长度为1,所以S[1]!=S[3],从而可知S[5]!=S[3],因此以S[4]为中心的最长回文子串的长度为1。


由S[1]推S[5]:

以S[1]为中心的最长回文子串的长度为3(S[0]~S[2]:"gxg"),将这一段以S[3]为中心翻转过去,会变成S[4]~S[6],所以以S[5]为中心的最长回文子串的长度至少为3。

之后,我们以S[5]为中心,向两边拓展,得出以S[5]为中心的最长回文子串的长度为7。


定义半径R:

对于S[i],R[i]指的是以S[i]为中心的最大回文的长度/2。

以S=“gxgagxgag”为例,R[0]=1,R[1]=2,R[2]=1,R[3]=4...


假设我们已经得到了以0~i每个点为中心的最长回文子串的长度。

j=max{i + R[i]}

对于k:

如果k>j,则意味着之前没有任何一个中心拓展到k,因此令R[k]=1,再不断朝两边拓展;

如果k<=j,则意味着至少有一个中心拓展到k,设这个中心为c,则可以由2*c-k(以c为中心的对称点)推k。

1.R[2*c-k]<j-k,则R[k]=R[2*c-k](对应上例中S[4]的情况); 

2.R[2*c-k]>=j-k,则令R[k]=j-k,再不断朝两边拓展(对应上例中S[5]的情况)。


到目前为止我们都只是考虑了奇回文串,形如:"aba",还没有考虑偶回文串的情况,形容:"abba"。

一种巧妙的方法可以让偶回文串变为奇回文串:插入无关字符,"abba"==>"#a#b#b#a#"。

这样,我们就只用考虑奇回文串了。


综上,这就是经典的Manacher算法。


class Solution {
public:
    string longestPalindrome(string s) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        int length = 2 * s.size() + 2;
        
        char* ch = new char[length + 1];
        ch[0] = '*';
        for (int i = 0; i < s.size(); i++) {
            ch[2 * i + 1] = '#';
            ch[2 * i + 2] = s[i];
        }
        ch[length - 1] = '#';
        ch[length] = '\0';
        
        int* p = new int[length];
        memset(p, 0, sizeof(int) * length);
        
        int maxIdx = 0;
        int symmetric = 0, center = 0;
        for (int i = 1; i < length; i++) {
            if (symmetric > i) {
               	p[i] = p[2 * center - i] < symmetric - i ? p[2 * center - i] : symmetric - i;
            }
            else {
            	p[i] = 1;
            }
            while (ch[i + p[i]] == ch[i - p[i]]) {
                p[i]++;
            }
            if (i + p[i] > symmetric) {
                symmetric = i + p[i];
                center = i;
            }
            if (p[i] > p[maxIdx]) {
                maxIdx = i;
            }
        }
        int temp = p[maxIdx];
        
        delete ch;
        delete p;
        
        return s.substr((maxIdx >> 1) - (temp >> 1), temp - 1);
    }
};


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值