问题链接
题目解析
求字符串的最长回文子串长度。
解题思路
回文:正读反读一模一样。
子串:原字符串中的一段连续字符串。
方法也有很多种,简单介绍一下。
方法一:DP
DP[i][j]定义成子串[i, j]是否是回文串。外循环 (i)从 (n-1) 往 (0) 遍历,内循环 (j) 从 (i) 往 (n-1) 遍历,若s[i]==s[j]:
- 若i==j,则dp[i][j]=true;
- 若i和j是相邻的,则dp[i][j]=true;
- 若i和j中间只有一个字符,则dp[i][j]=true;
- 否则,检查dp[i+1][j-1]是否为true,若为true,那么dp[i][j]就是true。
前三条可以合并,即 (j-i ≤ 2)。求得dp[i][j]真值后,如果其为true,判断长度,更新左右界。
注意一个小问题,若最长长度相等,则选择靠前的回文子串。
时间复杂度:(O(n^2))。
方法一参考代码:
class Solution {
public:
string longestPalindrome(string s) {
int len = s.size();
int longest = 0, left = 0, right = 0;//最长长度,左界,右界
vector<vector<bool>> dp(len, vector<bool>(len, false));
for (int i = len - 1; i >= 0; --i) {
for (int j = i; j < len; ++j) {
dp[i][j] = (s[i] == s[j]) && (j - i <= 2 || dp[i + 1][j - 1]);
if (dp[i][j] && longest <= j-i+1) {
longest = j-i+1;
left = i;
right = j;
}
}
}
return s.substr(left, right - left + 1);
}
};
方法二:回文中心法
本题可以不用DP,而是采用一种巧妙的方法:回文中心法。什么意思呢?考虑不同的回文中心,往两边扩散,求得回文数。需要考虑两种情况:如果是奇数长度回文串,了么回文中心为最中间的一个字符;如果是偶数长度回文串,这回文中心为最中间的两个字符。
时间复杂度:(O(n^2))。
class Solution {
public:
string longestPalindrome(string s) {
int len = s.size();
int longest = 0, left = 0, right = 0;//最长长度,左界,右界
for (int i = 0; i < len; ++i) {
int mid1 = i, mid2 = i, longX = -1;//奇数
while (mid1 >= 0 && mid2 < len && s[mid1] == s[mid2]) {
--mid1; ++mid2; longX +=2;
}
if (longest < longX) {
longest = longX;
if (longX > -1) mid1++, mid2--;
left = mid1;
right = mid2;
}
mid1 = i, mid2 = i+1, longX = 0;//偶数
while (mid1 >= 0 && mid2 < len && s[mid1] == s[mid2]) {
--mid1; ++mid2; longX += 2;
}
if (longest < longX) {
longest = longX;
if (longX > 0) mid1++, mid2--;
left = mid1;
right = mid2;
}
}
return s.substr(left, right - left + 1);
}
};
方法三:马拉车算法
神奇的算法,先马一下,学会再写上。听说时间复杂度是 (O(n))。
好了,学到了,请参考:什么是马拉车算法?
方法三参考代码:
class Solution {
public:
string longestPalindrome(string s) {
//预处理
string t = "#";
for (int i = 0; i < s.size(); ++i) {
t += s[i];
t += "#";
}
vector<int> RL(t.size(), 0);
int MaxRight = 0, pos = 0;
int resLen = 0, resCenter = 0;
for (int i = 0; i < t.size(); ++i) {
RL[i] = MaxRight > i ? min(RL[2 * pos - i], MaxRight - i) : 1;
while (i-RL[i] >=0 && i+RL[i] < t.size() && t[i + RL[i]] == t[i - RL[i]])//扩展,注意边界
++RL[i];
//更新最右端及其中心
if (MaxRight < i + RL[i] -1) {
MaxRight = i + RL[i] -1;
pos = i;
}
if (resLen < RL[i]) {
resLen = RL[i];
resCenter = i;
}
}
return s.substr((resCenter - resLen + 1) / 2 , resLen - 1);
}
};
LeetCode All in One题解汇总(持续更新中...)
本文版权归作者AlvinZH和博客园所有,欢迎转载和商用,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.