题目描述
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: “aba” 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
思路一
常见错误
有些人会忍不住提出一个快速的解决方案,不幸的是,这个解决方案有缺陷(但是可以很容易地纠正):
反转S,使之变成S′。找到S 和S′ 之间最长的公共子串,这也必然是最长的回文子串。
这似乎是可行的,让我们看看下面的一些例子。
例如,S = “caba”, S' = “abac”;
S 以及S′之间的最长公共子串为“aba”,恰恰是答案。
尝试一下这个例子:S= “abacdfgdcaba”,
S' = “abacdgfdcaba”;
S 以及 S′之间的最长公共子串为 “abacd” “abacd”,显然,这不是回文。
当 S 的其他部分中存在非回文子串的反向副本时,最长公共子串法就会失败。为了纠正这一点,每当我们找到最长的公共子串的候选项时,都需要检查子串的索引是否与反向子串的原始索引相同。如果相同,那么我们尝试更新目前为止找到的最长回文子串;如果不是,我们就跳过这个候选项并继续寻找下一个候选。
这给我们提供了一个复杂度为 O(n^2) 动态规划解法,它将占用 O(n^2)的空间(可以改进为使用 O(n) 的空间)。
思路二 (动态规划)
为了改进暴力法,我们首先观察如何避免在验证回文时进行不必要的重复计算。考虑“ababa”
这个示例。如果我们已经知道“bab”
是回文,那么很明显,“ababa”
一定是回文,因为它的左首字母和右尾字母是相同的。
我们给出 P(i,j)P(i,j) 的定义如下:
如果子串是回文子串其他情况
f
(
i
,
j
)
=
{
t
r
u
e
如果子串Si...Sj是回文子串
f
a
l
s
e
其他情况
f(i,j)= \begin{cases} true& \text{如果子串Si...Sj是回文子串}\\ false& \text{其他情况} \end{cases}
f(i,j)={truefalse如果子串Si...Sj是回文子串其他情况
因此,
P(i,j) = (P(i+1,j-1) and S_i==S_j)
基本示例如下:
P(i,j) = true;
P(i,i+1) = (S_i == S_i+_1)
这产生了一个直观的动态规划解法,我们首先初始化一字母和二字母的回文,然后找到所有三字母回文,并依此类推…
复杂度分析
时间复杂度:O(n^2)
,这里给出我们的运行时间复杂度为 O(n^2)
。
空间复杂度:O(n^2)
,该方法使用O(n^2)
的空间来存储表。
具体代码
class Solution {
public:
string longestPalindrome(string s) {
if(s.empty()) return s;
int dp[s.size()][s.size()] = {0};
int left = 0,right = 0,len = 0;
for(int i=0;i<s.size();++i){
for(int j=0;j<i;++j){
dp[j][i] = (s[i] == s[j] && (i-j<2) || dp[j+1][i-1]);
if(dp[j][i] && len < i-j+1){
len = i-j+1;
left = j;
right = i;
}
}
dp[i][i] = 1;
}
return s.substr(left,right-left+1);
}
};