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.youkuaiyun.com/pickless/article/details/9040293
另外再推荐一篇其他不错的有关这个算法的博客: http://blog.youkuaiyun.com/ggggiqnypgjg/article/details/6645824
还有一个简答的O(n * n)的算法,面试比较实用: http://blog.youkuaiyun.com/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);
}
};