Fork me on GitHub

【LeetCode】5.最长回文子串

题目描述

给定一个字符串 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”,显然,这不是回文。

当 SS 的其他部分中存在非回文子串的反向副本时,最长公共子串法就会失败。为了纠正这一点,每当我们找到最长的公共子串的候选项时,都需要检查子串的索引是否与反向子串的原始索引相同。如果相同,那么我们尝试更新目前为止找到的最长回文子串;如果不是,我们就跳过这个候选项并继续寻找下一个候选。

这给我们提供了一个复杂度为 O(n^2) 动态规划解法,它将占用 O(n^2)的空间(可以改进为使用 O(n) 的空间)。

思路二 (动态规划)

为了改进暴力法,我们首先观察如何避免在验证回文时进行不必要的重复计算。考虑“ababa” 这个示例。如果我们已经知道“bab”是回文,那么很明显,“ababa”一定是回文,因为它的左首字母和右尾字母是相同的。

我们给出 P(i,j)P(i,j) 的定义如下:

如果子串是回文子串其他情况
$$f(i,j)=
\begin{cases}
true& \text{如果子串Si…Sj是回文子串}\
false& \text{其他情况}
\end{cases}$$

因此,

1
P(i,j) = (P(i+1,j-1) and S_i==S_j)

基本示例如下:

1
2
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)的空间来存储表。

具体代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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);
}
};

本文标题:【LeetCode】5.最长回文子串

文章作者:LiuXiaoKun

发布时间:2019年04月22日 - 17:04

最后更新:2019年04月24日 - 17:04

原始链接:https://LiuZiQiao.github.io/2019/04/22/【LeetCode】5-最长回文子串/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%