力扣Hot100-5 最长回文子串
全部刷题与学习记录
原题目
题目地址:5. 最长回文子串
给你一个字符串 s
,找到 s
中最长的回文子串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
考查知识点
动态递归
好的解法
以往动态递归的题目中,题目给出一维环境(如斐波那契数),dp数组就是一维数组,题目是二维环境(矩阵路径),dp数组就是二维数组
这道题解剑走偏锋,环境是一维(字符串),dp数组却是二维的
对于动态规划,先用【动规五步法】来分析:
1、确定dp数组及下标含义:dp[i][j]
表示字符串s
的第i
个字母到第j
个字母组成的子串;dp[i] [j] = 1表示该子串是回文串,值为0表示其他情况(有两种可能,s[i] [j]不是回文串 || i > j, 此时s[i] [j]不合法)
2、确定递推公式:一个回文串必然是关于中心对称的,只有 s[i+1:j-1](更小范围上的dp[i] [j])是回文串,并且 s 的第 i 和 j个字母相同时,s[i:j]才会是回文串。递推公式为:dp[i][j] = (dp[i+1][j-1] && s[i] == s[j])
3、确定dp数组初始化:当回文串长度为0(代表只有一个字符)时,也算是特殊的回文串,dp[i] [j] = 1;当回文串长度是1(代表有两个字符)时,只要这两个字符相等,那么也是回文串dp[i] [j] = 1
4、确定dp数组遍历顺序:题解中使用的遍历顺序很新,一般来说二维dp数组都是从左到右、从上到下来遍历,但是字符串是一位的,在一维环境中使用二维dp数组,这里就给出了一种以前没见过的遍历顺序。先遍历子串长度,再遍历子串左起始位置。
5、举例推导dp数组
跳过
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
// dp[left][right]标记从i到j是否是字串
vector<vector<int>> dp(n, vector<int>(n));
string ans;
// length表示判断的字串的长度
// left表示字串的左边起始位置
// right表示字串的右边起始位置
for(int length = 0; length < n; length++){
for(int left = 0; left + length < n; left++){
int right = left + length;
// 即字符串长度为1时,矩阵对角线
if(length == 0) dp[left][right] = 1;
// 字符串长度为2的时候,只需判断两者是否相等
else if(length == 1) dp[left][right] = (s[left] == s[right]);
else{ // 字符串长度大于等于3之后
// 其是否是回文串取决于当前left和right及更小一号的字符串
// 更新参考值为矩阵的左下方
dp[left][right] = (s[left] == s[right] && dp[left + 1][right - 1]);
}
// 如果当前left位置到right位置的字串能够构成回文串,并且现在长度+1后大于之前记忆中的子回文串的长度,那么更新回文串!
// 这里也可以优化成记录起始位置和长度的两个int,返回时再截取
if(dp[left][right] && (length + 1) > ans.size()){
ans = s.substr(left, length + 1);
}
}
}
return ans;
}
};