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.
如果我们使用暴力解法,那么如果S的长度为n,总共有n*(n-1)/2个子串,子串的长度在1到n之间。所以时间复杂度是n的3次方。当然,这个方法实在是太傻了一点,我甚至想不出怎么写这个很傻的想法。所以回到一个大部分人都能想到的方法,从S的每一个位置出发,向两边寻找,然后记录最大的回文子串。
class Solution {
public:
string longestPalindrome(string s){
//If there is only one character or none, just return it.
if(s.size() <= 1)
return s;
//find the longest string with odd number, it means that there is a single character, e.g. aba, abcba.
int maxcentersingle = 0;
int maxlengthsingle = 0;
for (int ii = 0; ii < s.size(); ii ++)
{
int length = 0;
for (int jj = 0; jj <= ii && ii + jj < s.size(); jj ++)
{
if(s[ii + jj] == s[ii - jj])
length ++;
else
break;
}
if(length > maxlengthsingle)
{
maxlengthsingle = length;
maxcentersingle = ii;
}
}
//find the longest string with even number, it means that there is no single character, e.g. abba, abccba.
int maxcenterdual = 0;
int maxlengthdual = 0;
for (int ii = 0; ii < s.size(); ii ++)
{
int length = 0;
for (int jj = 0; jj <= ii && ii + jj + 1 < s.size(); jj ++)
{
if(s[ii + jj + 1] == s[ii - jj])
length ++;
else
break;
}
if(length > maxlengthdual)
{
maxlengthdual = length;
maxcenterdual = ii;
}
}
//If even string is longer.
if(maxlengthdual >= maxlengthsingle)
{
return s.substr(maxcenterdual - maxlengthdual + 1, 2 * maxlengthdual);
}
//If odd string is longer.
return s.substr(maxcentersingle - maxlengthsingle + 1, 2 * maxlengthsingle - 1);
}
}
当然还有线性规划方法,也是n的二次方,但是空间复杂度比上面的要大。
另外,有一个O(n)的算法,Manacher 线性算法。这个算法的关键点:
position[ii] = center + maxlen > ii ? min(position[mirror], center + maxlen - ii) : 0;
Mancher算法和上面的从中心开始找的算法的不同点在于上面的算法在每一个点,都是从相邻的位置开始向两边扩展,而Mancher算法则不同,它从上面这个式子里把以前已经记录的一些信息利用起来了。
center 是在已经有的数列中找到的最大回文的中心,从代码里你可以看到,最初的时候它也是通过和上面算法一样的从相邻位置往两边扩张得到的。Mancher算法只是对
center + maxlen > ii
这种情况进行优化,否则,它还是按照上面的算法进行。这个式子的意思是,如果当前的位置在最大子串的范围内,也就是说刚才我往两边扩展的时候已经经过这个点。既然是回文,那么这个点的回文长度可能会等于和它在这个回文中对应位置position[mirror]的回文长度,当然,它不能越过center + maxlen的位置。所以就得到了上面的式子。
如果你还有什么不明白的,可以参考一些文献和博客。
http://www.cnblogs.com/daoluanxiaozi/p/longest-palindromic-substring.html
http://www.akalin.cx/longest-palindrome-linear-time
class Solution {
public:
//abc => ^#a#b#c#$
string preprocessString(string s)
{
string newstr = "^#";
for (int ii = 0; ii < s.size(); ii ++)
{
newstr += s[ii];
newstr += "#";
}
newstr += "$";
return newstr;
}
string longestPalindrome(string s)
{
string newStr = preprocessString(s);
vector<int> position(newStr.size(), 0);
int center = 0;
int maxlen = 0;
for (int ii = 1; ii < newStr.size(); ii ++)
{
int mirror = 2 * center - ii;
position[ii] = center + maxlen > ii ? min(position[mirror], center + maxlen - ii) : 0;
while(newStr[ii - position[ii] - 1] == newStr[ii + position[ii] + 1])
{
position[ii] ++;
}
if(position[ii] > maxlen)
{
maxlen = position[ii];
center = ii;
}
}
maxlen = 0;
center = 0;
for (int ii = 0; ii < newStr.size(); ii ++)
{
if(position[ii] > maxlen)
{
maxlen = position[ii];
center = ii;
}
}
//newstr (1, 2) 对应 s(0), (3, 4) => (1), ...
return s.substr((center - 1 - maxlen)/2, maxlen);
}
};