给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。
示例 1:
输入: "babad" 输出: "bab" 注意: "aba"也是一个有效答案。
示例 2:
输入: "cbbd" 输出: "bb"
思路:首先给出动态规划解法的时间复杂度O(n^2),需要维护一个二维数组dp[i][j],其中dp[i][j]表示从字符串的s[i]到s[j](包含s[j])是回文序列,dp[i][j]是回文序列,则dp[i][j]赋值为true,我们只需要返回i和j间隔最大的子字符串即可。
这里先给出递归公式:
dp[i, j] = true if i == j
= s[i] == s[j] if j = i + 1
= s[i] == s[j] && dp[i + 1][j - 1] if j > i + 1
解释:如果i==j,即中只有一个字符的情况,s[i]当然等于自身,所以赋值为true。如果j=i+1(这里为什么相邻的要单独讨论,因为相邻的没法找到他的子集的情况,及s[i+1]到s[j-1]这种情况,比较特殊,所以单独考虑),如果相等就把dp[i][j]赋值为true。最后一种情况,如果i和j相差3个及以上,就判断他的子集dp[i+1][j-1](想等于向内收缩)以及自身是否相等,如果相等,就把dp[i][j]赋值为true,以下为一个图例说明情况。
由于我们判断的是序列[i,j]是否是回文序列,所以对于左下角部分不用讨论(红色圆圈所示),对二维数组的遍历我们采用红色线所示的路径进行,而不是传统的如下遍历方式:
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
//blabla
}
}
因为如下图所示,我们在递归条件式中可以看到,当前状态更新需要用到dp[i+1][j-1]的值,从图像上来看就是需要左下角状态的信息(如图所示,绿色更新需要需要橘黄色的状态作为前提(假设不考虑j-i>2的情况,这里只是示例)),所以我们需要由底向上,由左向右的更新方式。
参考代码:
string longestPalindrome(string s) {
string res;
bool **dp = new bool *[s.size()];
for (int i = 0; i < s.size(); i++) {
dp[i] = new bool[s.size()]{false};
}
int n = s.size();
for (int i = n - 1; i >= 0; i--) {
for (int j = i; j < n; j++) {
dp[i][j] = s[i] == s[j] && ((j - i) < 3 || dp[i + 1][j - 1]);
if (dp[i][j] && (res.size() == 0 || (j - i + 1) > res.size())) {
res = s.substr(i,j-i+1);
}
}
}
for (int i = 0; i < s.size(); i++) {
delete[] dp[i];
}
delete[] dp;
return res;
}