0%

Leetcode 5 Longest Palindromic Substring

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example 1:

1
2
3
Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.

Example 2:

1
2
Input: "cbbd"
Output: "bb"

1 动态规划1(超时)

注意到, 从i到j的字串中的最长回文子串要么是他自己, 要么是从i+1到j字符串的回文子串, 要么是i到j-1字符串的回文子串.

1
2
3
4
if string(i,j) is palindrome
dp[i][j] = string(i,j);
else
dp[i][j] = maxlen(dp[i+1][j], dp[i][j-1]);

但是这样做, 复杂度为n^3. (填充dp矩阵n^2, 每一次判断string(i,j)是不是回文需要n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Solution {
public:
string longestPalindrome(string s) {
if(s.empty()) return s;
const int n = s.size();
string dp[n][n];
for(int i = 0; i < n; ++i)
{
dp[i][i] = string(s.begin() + i, s.begin() + i + 1);
}

for(int c = 1; c < n; ++c)
{
for(int i = 0; i < n - c; ++i)
{
if(isPalindrome(s, i, i + c))
dp[i][i+c] = string(s.begin() + i, s.begin() + i + c + 1);
else
{
string s1 = dp[i+1][i+c];
string s2 = dp[i][i+c-1];
dp[i][i+c] = (s1.size() > s2.size()) ? s1 : s2;
}
}
}
return dp[0][n-1];
}
private:
bool isPalindrome(const string& s, int begin, int end)
{
while(end > begin)
{
if(s[end] == s[begin])
{
++begin;
--end;
}
else
return false;
}
return true;
}
};

2 动态规划2

改变动态规划1中的做法, 我们存储一个bool数组bool isPalindrome[i][j]表示从i到j的字串是否为palindrome.

那么, 首先可以得到isPalindrome[i][i] is always true. isPalindrome[i][i+1] = (s[i] == s[i+1])

其次, isPalindrome[i][j]可以由 isPalindrome[i+1][j-1]推导出来.

如果isPalindrome[i+1][j-1]为真, 并且s[i] == s[j], 那么就有isPalindrome[i][j] = true, 否则为false

根据此结论即可递推的求出所有的isPalindrome[i][j]. 找最长的即可.

时间和空间均为O(n^2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Solution {
public:
string longestPalindrome(string s) {
if(s.empty()) return s;
const int n = s.size();
bool isPalindrome[n][n];
for(int i = 0; i < n; ++i)
{
isPalindrome[i][i] = true;
}
for(int i = 0; i < n - 1; ++i)
{
isPalindrome[i][i+1] = (s[i] == s[i+1]);
}

for(int c = 2; c < n; ++c)
{
for(int i = 0; i < n - c; ++i)
{
isPalindrome[i][i+c] = (isPalindrome[i+1][i+c-1]) && (s[i] == s[i+c]);
}
}
int maxLen = INT_MIN;
int begin = 0;
int end = 0;
for(int i = 0; i < n; ++i)
{
for(int j = i; j < n; ++j)
{
if(isPalindrome[i][j])
{
if(maxLen < j - i + 1)
{
maxLen = j - i + 1;
begin = i;
end = j;
}
}
}
}
return string(s.begin() + begin, s.begin() + end + 1);
}
};

3 中心扩展法

考虑到每个回文子串都有一个中心, 这个中心可以是某个字符串中的字符, 也可能是某2个相邻字符中间处.

所以一共有2n-1个可能的中心. 对于这2n-1个中心, 每一个中心都向外扩展, 看看最大能扩展到什么长度, 然后找出最大的就可以

时间复杂度O(n^2), 空间O(1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Solution {
public:
string longestPalindrome(string s) {
if(s.empty()) return s;
const int n = s.size();
int maxLen = INT_MIN;
int begin = 0;
int end = 0;
for(int mid = 0; mid < n; ++mid)
{
int localLen = 1;
int localBegin = mid;
int localEnd = mid;
while(localBegin > -1 && localEnd < n && s[localBegin] == s[localEnd])
{
localLen = localEnd - localBegin + 1;
if(maxLen < localLen)
{
maxLen = localLen;
begin = localBegin;
end = localEnd;
}
--localBegin;
++localEnd;
}
}
for(int mid = 0; mid < n - 1; ++mid)
{
int localLen = 0;
int localBegin = mid;
int localEnd = mid + 1;
while(localBegin > -1 &&localEnd < n && s[localBegin] == s[localEnd])
{
localLen = localEnd - localBegin + 1;
if(maxLen < localLen)
{
maxLen = localLen;
begin = localBegin;
end = localEnd;
}
--localBegin;
++localEnd;
}
}
return string(s.begin() + begin, s.begin() + end + 1);
}
};