题目
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example:
Input: "babad" Output: "bab" Note: "aba" is also a valid answer.
Example:
Input: "cbbd" Output: "bb"
思路
1、暴力枚举法:两种循环分别枚举每种子字符串,然后再用O(n)的时间复杂度判断该子字符串是否是回文。暴力枚举法经过优化后虽然可以按照子字符串的长度递减次序进行枚举(这样一旦发现回文串就可以立即终止并返回),但是其时间复杂度依然高达O(n^3),空间复杂度则为O(1)。
2、动态规划法:设dp[i][j]表示从索引i到索引j之间的子字符串是否是回文串,则递推公式为:dp[i][j] = (s[i] == s[j]) && (j - i <= 2 || dp[i + 1][j - 1])。注意到计算dp[i][j]则必须首先知道dp[i+1][j-1]的值,所以我们可以让j作为外循环,其值从小到大依次递增。动态规划的时间复杂度是O(n^2),空间复杂度是O(n^2)。
3、中心展开法:尽管动态规划法表现的特别优雅,但是针对该题目,它仍然不是最优解法,因为中心展开法可以在保证时间复杂度为O(n^2)的情况下,将空间复杂度降为O(1)。其基本思路是:对字符串中的每个字符,试图计算以它为中心的最长回文串(注意需要分别考虑回文串长度为奇数和偶数两种情况)。
4、Manacher算法:该算法的时间复杂度和空间复杂度都可以做到O(n),可以在网上搜索参考具体做法。但是由于该算法相对比较复杂,个人觉得在面试的时候如果能完整写出动态规划法和中心展开法,就足以让面试官放你过关了。
代码
动态规划法:
class Solution {
public:
string longestPalindrome(string s) {
int length = s.length();
if(length == 0)
return "";
int max_length = 0;
string answer;
vector<vector<bool>> dp(length, vector<bool>(length, false));
for(int j = 0; j < length; ++j)
{
for(int i = 0; i <= j; ++i)
{
dp[i][j] = (s[i] == s[j]) && (j - i <= 2 || dp[i + 1][j - 1]);
if(dp[i][j] && max_length < j - i + 1)
{
max_length = j - i + 1;
answer = s.substr(i, max_length);
}
}
}
return answer;
}
};
中心展开法:
class Solution {
public:
string longestPalindrome(string s) {
int length = s.length();
if(length == 0)
return "";
int max_length = 0;
string answer;
for(int i = 0; i < length; ++i)
{
// odd case
int offset = 0;
while(i - offset >= 0 && i + offset < length && s[i - offset] == s[i + offset])
offset++;
if(max_length < 2 * offset - 1)
{
max_length = max(max_length, 2 * offset - 1);
answer = s.substr(i - offset + 1, max_length);
}
// even case
offset = 0;
int j = i + 1;
while(i - offset >= 0 && j + offset < length && s[i - offset] == s[j + offset])
offset++;
if(max_length < 2 * offset)
{
max_length = max(max_length, 2 * offset);
answer = s.substr(i - offset + 1, max_length);
}
}
return answer;
}
};