题目来源:https://leetcode-cn.com/problems/longest-palindromic-substring
题目描述
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”
示例 3:
输入:s = “a”
输出:“a”
示例 4:
输入:s = “ac”
输出:“a”
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母(大写和/或小写)组成
题目大意
- 经典动态规划题
- 可以思考一下,假设当某一子串已经是回文串,此时若将子串进行扩张,扩张后的子串是回文串的充要条件是子串是回文串且子串的两个端点字符相等(此处无需用双指针重新比较回文串,是因为原子串已满足)
- 状态方程如下,当i≥j的时候为True,此处其实可以不看i>j的情况,从最小的子问题,单个字符(s[0]或s[s.length()-1]都可以)组成的字符串为回文串开始处理,再逐渐扩展到F(0,s.length())
- 方程中F(i,j)表示原字符串中下标i到j所对应的子串
F ( i , j ) = { T r u e i ≥ j F ( i + 1 , j − 1 ) & & ( s [ i ] = = s [ j ] ) i < j F(i,j)= \left \{ \begin{array} {c}True{\quad}i≥j \\F(i + 1, j - 1)\&\&(s[i]==s[j]){\quad}i<j \end{array} \right. F(i,j)={ Truei≥jF(i+1,j−1)&&(s[i]==s[j])i<j
动态规划
- 记录满足回文子串此时的i和j,然后返回对应回文子串即可
class Solution {
public:
string longestPalindrome(string s) {
int len = s.size(), okLen = 0;
if (len == 1) return s;
vector<vector<bool>> record(len, vector<bool>(len, true));
string ans;
int maxi = 0, maxj = 0;
for (int i = len - 1 ; i >= 0 ; --i){
for (int j = i + 1 ; j < len ; ++j){
record[i][j] = (record[i + 1][j - 1] && s[i] == s[j]);
if(record[i][j] && okLen <= j - i + 1){
okLen = j - i + 1;
maxi = i;
maxj = j;
}
}
}
return s.substr(maxi, maxj - maxi + 1);
}
};
复杂度分析
- 时间复杂度:O(n^2)。n为数组的长度
- 空间复杂度:O(n^2)。n为数组的长度,维护了一个dp布尔类型的二维数组
中心扩展算法
- 以局部中心拓展到这个字符串,原理上是枚举两个扩散中心,向两边扩散,即以下两种,此方法比单纯的动态规划高效快速许多
c e n t e r ( i , i ) ( 奇 数 回 文 子 串 ) c e n t e r ( i , i + 1 ) ( 偶 数 回 文 子 串 ) center(i,i){\quad}(奇数回文子串) \\center(i,i+1){\quad}(偶数回文子串) center(i,i)(奇数回文子串)center(i,i+1)(偶数回文子串)
class Solution {
public:
pair<int, int> ExpandAroundCenter(const string& s, int left, int right){
int len = s.size();
while (left >= 0 && right < len && s[left] == s[right]){
--left;
++right;
}
return {
left + 1, right - 1};
}
string longestPalindrome(string s) {
int start = 0, end = 0, len = s.size();
for (int i = 0 ; i < len - 1; ++i){
auto [left1, right1] = ExpandAroundCenter(s, i, i);
auto [left2, right2] = ExpandAroundCenter(s, i, i + 1);
if(right1 - left1 >= end - start){
start = left1;
end = right1;
}
if(right2 - left2 >= end - start){
start = left2;
end = right2;
}
}
return s.substr(start, end - start + 1);
}
};
复杂度分析
- 时间复杂度:O(n^2)。枚举中心位置的个数是2(n-1),每一次向两边扩散检测是否是回文
- 空间复杂度:O(1)