LeetCode 热题 100_最长回文子串(93_5_中等_C++)
题目描述:
给你一个字符串 s,找到 s 中最长的 回文 子串。
输入输出样例:
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母组成
题解:
解题思路:
思路一(暴力破解法):
1、从长度最长的子串开始判断,然后让子串慢慢减小,直至找到第一个回文子串即为最长回文子串。
2、复杂度分析:
① 时间复杂度:O(n3),判断子串是否为回文子串最坏的情况为O(n),子串的长度从n开始减小最坏的情况为O(n),每个子串相同大小的子串的检查最坏的情况为O(n),所以为O(n3)。
② 空间复杂度:O(1)。
思路二(动态规划):
1、通过分析问题可得出,长度较长的子串是否为回文子串,可由其部分子串(中间的子串)推出,因此可采用动态规划来求解。
2、具体思路如下:
① 首先定义一个二维dp数组 dp[i][j] 代表从 i 开始到 j 结束的子串是否为回文子串。
② dp[ i ][ i ]=true,因只含有一个字符时必定为回文子串。
③ 因定义了i<=j,所以只需用到上三角矩阵
④ dp[ i ][ j ]的递推可以分为三种情况:
- 当s[ i ]!=s[ j ]时,dp[ i ][ j ] == false。
- 当s[ i ]==s[ j ] 且 len( j-i+1 ) <= 3时 dp[ i ][ j ] 为 true,含有三个或两个元素,两边元素相等必定为回文子串。
- 当s[ i ]==s[ j ] 且 len( j-i+1 ) > 3时需判断dp[ i+1 ][ j-1 ]是否是回文子串 dp[ i ][ j ] = dp[i+1][j-1]。
在此过程中记录最长回文子串的开始下标和长度。
因dp[i][j]=dp[i+1][j-1],当前位置取决于左下角位置所以我们需要从左下角往右上角进行递推和遍历。
3、复杂度分析
① 时间复杂度:O(N),其中 N 是数组中的元素数量。因遍历一遍数组且哈希表的查找为O(1)
② 空间复杂度:O(N),其中 N 是数组中的元素数量。创建哈希表的空间。
代码实现
代码实现(思路一(暴力破解法)):
class Solution1{
private:
// 检查字符串子串是否为回文
bool isPalindromeSubstring(string &str, int left, int right){
// 从两端向中间检查字符是否相等
while (left <= right){
if (str[left] != str[right]){
return false; // 如果有不同的字符,返回false
}
++left; // 向右移动左指针
--right; // 向左移动右指针
}
return true; // 如果没有发现不同的字符,说明是回文
}
public:
// 返回给定字符串中的最长回文子串
string longestPalindrome(string s){
int left, right, length = s.size(); // 计算字符串的长度
// 从最大长度的子串开始尝试
for (length; length >= 1; length--){
left = 0;
right = left + length - 1; // 初始化右指针
// 确保右指针不越界
while (right < s.size()){
// 如果找到回文子串,返回该子串
if (isPalindromeSubstring(s, left, right)){
return s.substr(left, right - left + 1); // 提取并返回回文子串
}
left++; // 移动左指针,检查下一个子串
right++; // 移动右指针
}
}
return "false"; // 如果没有找到回文子串,返回"false"
}
};
代码实现(思路二(动态规划)):
class Solution2{
public:
string longestPalindrome(string s) {
int maxLen = 1, startIndex = 0; // 初始化最长回文子串的长度和起始索引
vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false)); // 创建一个二维布尔数组 dp,dp[i][j]表示子串s[i...j]是否为回文
// 初始化每个字符自己是回文的
for (int i = 0; i < s.size(); i++){
dp[i][i] = true; // 每个字符本身是回文
}
// 枚举不同长度的子串,长度从2开始到字符串的长度
for (int len = 2; len <= s.size(); len++){
// 枚举所有可能的起始位置
for (int i = 0; i <= s.size() - len; i++){
int j = i + len - 1; // 计算当前子串的结束位置
// 如果子串的两端字符相等,进一步检查
if(s[i] == s[j]){
if (len <= 3){
dp[i][j] = true; // 长度为2或3时,直接判断是否为回文
} else {
dp[i][j] = dp[i + 1][j - 1]; // 对于更长的子串,判断其内部子串是否为回文
}
}
// 如果当前子串是回文并且长度超过之前找到的最大回文长度,则更新最大回文长度和起始位置
if (dp[i][j] && len > maxLen){
maxLen = len; // 更新最长回文子串的长度
startIndex = i; // 更新最长回文子串的起始位置
}
}
}
return s.substr(startIndex, maxLen); // 返回最长回文子串
}
};
以思路二为例进行调试
#include<iostream>
#include<vector>
using namespace std;
class Solution2{
public:
string longestPalindrome(string s) {
int maxLen = 1, startIndex = 0; // 初始化最长回文子串的长度和起始索引
vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false)); // 创建一个二维布尔数组 dp,dp[i][j]表示子串s[i...j]是否为回文
// 初始化每个字符自己是回文的
for (int i = 0; i < s.size(); i++){
dp[i][i] = true; // 每个字符本身是回文
}
// 枚举不同长度的子串,长度从2开始到字符串的长度
for (int len = 2; len <= s.size(); len++){
// 枚举所有可能的起始位置
for (int i = 0; i <= s.size() - len; i++){
int j = i + len - 1; // 计算当前子串的结束位置
// 如果子串的两端字符相等,进一步检查
if(s[i] == s[j]){
if (len <= 3){
dp[i][j] = true; // 长度为2或3时,直接判断是否为回文
} else {
dp[i][j] = dp[i + 1][j - 1]; // 对于更长的子串,判断其内部子串是否为回文
}
}
// 如果当前子串是回文并且长度超过之前找到的最大回文长度,则更新最大回文长度和起始位置
if (dp[i][j] && len > maxLen){
maxLen = len; // 更新最长回文子串的长度
startIndex = i; // 更新最长回文子串的起始位置
}
}
}
return s.substr(startIndex, maxLen); // 返回最长回文子串
}
};
int main(int argc, char const *argv[])
{
string str= "babad";
Solution2 s;
cout<<s.longestPalindrome(str);
return 0;
}
LeetCode 热题 100_最长回文子串(93_5)原题链接
欢迎大家和我沟通交流(✿◠‿◠)