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计算了,这个大大减少了计算量。
如果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;
}};