LeetCode No5. 最长回文子串 题解
一、题目
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”
示例 3:
输入:s = “a”
输出:“a”
示例 4:
输入:s = “ac”
输出:“a”
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母(大写和/或小写)组成
二、解题思想
寻找回文串,核心就是串的对称。仅从一头找的话很难确定另一头的终点位置。利用回文串的对称性,我们可以
- 从两端出发向内寻找
- 也可以选定一个中心点向两端扩散。
![](https://img-blog.csdnimg.cn/775f31da7f6e4b3a977b7f121595131a.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAemhlbGlrdQ==,size_20,color_FFFFFF,t_70,g_se,x_16)
按照第一种方式(双指针),实现起来我们会发现,两端的位置也难以确定。例如,我们选定主串的头和尾当初始子串,如果该子串不满足回文,那么下一个子串该如何寻找呢?
是左边向右移动一位?
![](https://img-blog.csdnimg.cn/9dc8f55fb5a64b31bfa028921bb44a98.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAemhlbGlrdQ==,size_20,color_FFFFFF,t_70,g_se,x_16)
还是右边向左移动一位?
![](https://img-blog.csdnimg.cn/5736f049935c47e4940b74c3a3555602.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAemhlbGlrdQ==,size_20,color_FFFFFF,t_70,g_se,x_16)
还是左右都移动一位?
![](https://img-blog.csdnimg.cn/d373776d3b084d0fb9e0e5c06a3bdb3c.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAemhlbGlrdQ==,size_20,color_FFFFFF,t_70,g_se,x_16)
再往后列举的话,相当于穷举了,因此看来这貌似并不是一个很好的解决办法。(至少粗略来看,目前没有什么思路)
因此,我们可以选择第二种方式,以每一个字符为中心,向两端扩张寻找最大回文子串,就像这样:
![](https://img-blog.csdnimg.cn/86a3490f79a8419883576322e9807b32.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAemhlbGlrdQ==,size_20,color_FFFFFF,t_70,g_se,x_16)
当然,你会发现,我们漏了一个回文串 “adccda”,因为这个回文串长度为奇数,因此我们还需要像这样寻找:
![](https://img-blog.csdnimg.cn/e24e5a64dc8d44a2ab5ddaf02e48b3ca.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAemhlbGlrdQ==,size_20,color_FFFFFF,t_70,g_se,x_16)
最后比较两种情况寻找的最长回文子串哪个更长就可以啦!
三、代码
class Solution {
public:
// 寻找奇数长度的回文子串
// s:主串
// index:中心出发点
// left:最长回文子串起始位置
// right:最长回文子串终点位置
// len:最长回文子串的长度
void oddPlalindrome(const string &s, int index, int &left, int &right, int &len) {
int t_len = 1, t_left = index, t_right = index; // 临时长度、子串左右位置
while (t_left >= 1 && t_right < s.size() - 1) // 保证子串不出界
{
if (s[t_left - 1] != s[t_right + 1]) break; // 向两端延展,如果不同则直接退出循环
// 更新临时长度和子串位置
t_len += 2;
--t_left; ++t_right;
}
// 如果这次长度更长,更新子串位置和长度
if (t_len >= len)
{
len = t_len;
left = t_left;
right = t_right;
}
}
// 寻找偶数长度的回文子串
void evenPlalindrome(const string &s, int index, int &left, int &right, int &len) {
int t_len = 2, t_left = index, t_right = index + 1; // 初始长度 t_len 设为 2
if (s[t_left] != s[t_right]) return; // 如果相邻的两个字符不一样,直接退出函数
// 同上
while (t_left >= 1 && t_right < s.size() - 1)
{
if (s[t_left - 1] != s[t_right + 1]) break;
t_len += 2;
--t_left; ++t_right;
}
if (t_len >= len)
{
len = t_len;
left = t_left;
right = t_right;
}
}
string longestPalindrome(string s) {
int left, right, len = 0;
// 以每个字符为中心,扩散寻找最大子串
for (int i = 0; i < s.size(); ++i)
{
oddPlalindrome(s, i, left, right, len);
evenPlalindrome(s, i, left, right, len);
}
return string(s.begin() + left, s.begin() + right + 1);
}
};
四、复杂度分析
时间复杂度:
一般情况下,时间复杂度为 O(n),因为 oddPlalindrome 和 evenPlalindrome 两个函数的耗时趋近于常数。
最坏情况下,时间复杂度为 O(n2),此时串中具有较长的连续相同字符或间歇周期性字符,例如 “aaaaaaaaaa”,“ababababababa”,可以在源代码基础上添加判断情况进行优化。
空间复杂度:
oddPlalindrome 和 evenPlalindrome 函数没有开辟新的大内存空间,因此复杂度为 O(1)。
五、算法评价
LeetCode 中:
执行用时:16 ms, 在所有 C++ 提交中击败了92.31%的用户
内存消耗:6.9 MB, 在所有 C++ 提交中击败了95.50%的用户
提交了很多次,整体时间稳定在 16 ~ 20ms,内存消耗稳定在 6.9 ~ 7.0ms。
相关文章
- LeetCode每日一练 —— 链表中倒数第 k 个结点
- 【LeetCode】电话号码的字母组合 [M](深度优先遍历)
- 【LeetCode】最长回文子串 [M](Manacher算法)
- 【LeetCode】最长连续序列 [M](数据结构设计)
- 矩阵中和能被 K 整除的路径 leetcode第314周赛第四题
- LeetCode_贪心算法_简单_409.最长回文串
- LeetCode_动态规划_中等_673.最长递增子序列的个数
- LeetCode_滑动窗口_中等_904.水果成篮
- LeetCode_动态规划_简单_674.最长连续递增序列
- LeetCode_位运算_中等_137.只出现一次的数字 II
- LeetCode_滑动窗口_中等_424.替换后的最长重复字符
- LeetCode_动态规划_中等_1143.最长公共子序列
- LeetCode_Boyer-Moore 投票算法_中等_229.求众数 II
- LeetCode_排序_中等_179.最大数
- LeetCode·516.最长回文子序列·动态规划
- LeetCode·377.组合总和 IV ·动态规划
- leetcode || Spiral Matrix
- 【LeetCode】- Length of Last Word(最后一个单词的长度)
- Leetcode最长上升子序列LIS
- [LeetCode] 687. Longest Univalue Path 最长唯一值路径
- [LeetCode] 567. Permutation in String 字符串中的全排列
- [LeetCode] 298. Binary Tree Longest Consecutive Sequence 二叉树最长连续序列
- [LeetCode] 100. Same Tree 相同树
- [LeetCode] 387. First Unique Character in a String 字符串的第一个唯一字符
- [LeetCode] 452. Minimum Number of Arrows to Burst Balloons 最少箭数爆气球
- [LeetCode] 516. Longest Palindromic Subsequence 最长回文子序列
- [LeetCode] 32. Longest Valid Parentheses 最长有效括号
- leetcode 5 最长回文子串
- leetcode 227 基本计算器II
- #leetcode 637二叉树的层平均值