题目信息
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
示例 3:
输入:s = "a"
输出:"a"
示例 4:
输入:s = "ac"
输出:"a"
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母(大写和/或小写)组成
解题思路
法1:动态规划
我们定义dpij为si到sj这段字符串是否为回文串。
构造状态转移方程:
若si == sj,dpij = dpi+1j-1(向两边各延伸一个字符)。
否则,dpij = 0(回文串两边字符必须相等)。
初始化:
- ∀(任意)dpii = 1(所有单个字符均为回文串)
- 若si = si+1, dpii+1 = 1,否则为0。
法2:中心拓展
回文串中心的两侧互为镜像。因此,回文可以从他的中心展开。我们可以枚举这(2n - 1)个中心(一个个元素为中心n中,两个(n - 1)种),依次展开即可。
代码实现
法1:动态规划
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
if (n == 0 || n == 1) { // 空串直接返回,一个字符的肯定是回文串
return s;
}
int maxl = 1; // 回文串最大长度
int st = 0; // 回文串最大长度时的起点
vector<vector<bool>> dp(n, vector<bool>(n));
// 初始化
for (int i = 0; i < n; ++i) {
dp[i][i] = true;
}
for (int l = 2; l <= n; ++l) { // 枚举回文串长度
for (int i = 0; i < n; ++i) { // 枚举回文串起点
int j = l + i - 1; // 用长度和起点确定终点
if (j >= n) { // 越界直接停止
break;
}
// 状态转移
if (s[i] != s[j]) {
dp[i][j] = false;
} else {
if (j - i < 3) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i + 1][j - 1];
}
}
// 如果目前长度比原来最大长度还要大,那么就将l赋值给maxl
if (dp[i][j] && j - i + 1 > maxl) {
maxl = j - i + 1;
st = i;
}
}
}
return s.substr(st, maxl);
}
};
法2:中心拓展
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
if (n == 0 || n == 1) { // 空串直接返回,一个字符的肯定是回文串
return s;
}
int maxl = 1; // 回文串最大长度
int st = 0; // 回文串最大长度时的起点
int en = 0; // 终点
for (int i = 0; i < n; ++i) {
int l1 = get(s, i, i); // 一个元素为中心时的长度
int l2 = get(s, i, i + 1); //两个元素为中心时的长度
maxl = max(maxl, max(l1, l2));
if (maxl > en - st + 1) {
st = i - (maxl - 1) / 2;
en = i + maxl / 2;
}
}
return s.substr(st, maxl);
}
// 求以s[[l, r]]为中心的回文串最大长度
int get(string s, int _l, int _r) {
int n = s.size();
int l = _l, r = _r; // 不能改变参数值,备份
while (l >= 0 && r < n && s[l] == s[r]) {
--l;
++r;
}
return r - l - 1;
}
};