leetcode--Longest Palindromic Substring

1.题目描述

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.

2.解法分析

拿到题目的一瞬间,我就想到了两个办法,但都看起来不是那么好的方法,方法一是求出输入串和输入串逆置串(就是将输入串逆序)的最长公共字串,这个算法很容易实现,但是时间复杂度过高,还有一种就是后缀数组的方法,求出最长后缀即可,但是这种方法比较麻烦。做了这么长时间的leetcode,我的经验告诉我这个题目应该会有更加精妙的解法,希望能理解。

果不其然,网上是有另外一种算法的,这个算法深入挖掘了回文的性质,针对这个题目,最简单的算法莫过于遍历字符串的每一位,然后以每一位为中心,两边扩展来求最长的回文子串,这样做的时间复杂度当然是O(N2),那么在遍历的过程中,有没有方法利用之前得到的结果呢?

答案是有的!网上传有一种Manacher算法--O(n)回文子串算法,其他的解法都是这种,只不过每个人都讲得比较混乱,唯有这个虽然也混乱,但是图画得比较清晰,我就借用了这篇文章的图了。

请看下图:

假设用一个数组p(长度为字符串长度,注意,在处理之前,为了使字符串长度的奇偶不影响处理过程,进行了插入“#”的预处理操作,一会看代码就明白了为什么这么处理了),p[i]表示以s[i]为中心的最长回文子串,在计算p[i]之时我们已知了p[0]….p[i-1],以及当前所有计算出来的最长回文子串所能延伸到的最右位置mx以及其对应的回文中心id(在代码里,我将它称为center),那么,i关于id的对称位置j=2*id-i, 且如果mx>i,必然有p[i]至少等于min(p[j],mx-i),如果mx<=i,那只有老老实实从p[i]=1计算了,这个大大减少了计算量。

 

image

image

如果mx>i,那么mx之外的字符是没有经过比较的,所以p[i]应该不比mx-i大,为啥?分析如下,如果p[j]<mx-i,说明整个以j为中心的回文子串都在以id为中心的回文子串之内。那么直接取p[j]作为p[i]的值肯定没错,反之,说明,p[j]有一部分伸出,这部分得减掉。

代码如下:

class Solution {
public:
    string longestPalindrome(string s) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        string s2;
        s2.push_back('#');
        for(int i=0;i<s.length();++i)
        {
            s2.push_back(s[i]);
            s2.push_back('#');
        }
        
        vector<int> p;
        p.assign(s2.length(),0);
        
        int mx=0;
        int center=0;
        
        int max=1;int loc=0;
        
        for(int i=0;i<s2.length();i++)
        {
            if(mx>i)
            {
                p[i]=min(p[2*center-i],mx-i);
            }
            else p[i]=1;
            
            while((i-p[i])>=0&&(i+p[i])<s2.length())
            {
                if(s2[i-p[i]]==s2[i+p[i]])p[i]+=1;
                else break;
            }
            
            if((i+p[i]-1)>mx)
            {
                mx=i+p[i]-1;center=i;
            }
            
            if(p[i]>max)
            {
                max=p[i];
                loc=i;
            }
        }
        
        string result;
        for(int i=loc-max+1;i<=loc+max-1;++i)
        {
            if(s2[i]!='#')result.push_back(s2[i]);
        }
        
        return result;
        
        
    }
};
posted @ 2013-08-19 21:49  曾见绝美的阳光  阅读(426)  评论(0编辑  收藏  举报