最长回文子串
给你一个字符串 s
,找到 s
中最长的 回文 子串。
示例 1:
输入:s = "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd" 输出:"bb"
中心拓展法:
class Solution {
public:string longestPalindrome(string s) {// 边界条件:空字符串直接返回if(s.size() < 1)return s;// 变量定义:int Len1 = 1, Len2 = 1; // 分别记录奇数/偶数长度回文的当前最大长度(初始为1,单个字符)int MAXLen = 1; // 全局最长回文的长度(初始为1)int start = 0; // 全局最长回文的起始位置// 遍历每个字符,将其作为回文中心进行扩展for(int i = 0; i < s.size(); i++) {// 情况1:处理奇数长度的回文(中心是单个字符s[i])int l = i - 1, r = i + 1; // 从中心向左右两侧扩展while(l >= 0 && r < s.size()) { // 确保不越界if(s[l] == s[r]) { // 两侧字符对称,继续扩展Len1 = r - l + 1; // 更新当前奇数回文的长度l--; r++; // 继续向两侧扩展} else {break; // 两侧字符不对称,停止扩展}}// 情况2:处理偶数长度的回文(中心是两个相邻字符s[i]和s[i+1])l = i, r = i + 1; // 从相邻两个字符开始向两侧扩展while(l >= 0 && r < s.size()) { // 确保不越界if(s[l] == s[r]) { // 两侧字符对称,继续扩展Len2 = r - l + 1; // 更新当前偶数回文的长度l--; r++; // 继续向两侧扩展} else {break; // 两侧字符不对称,停止扩展}}// 计算当前中心(i)下能找到的最长回文长度int currentMax = max(Len1, Len2);// 如果当前中心找到的回文比全局最长更长,则更新全局记录if (currentMax > MAXLen) {MAXLen = currentMax; // 更新全局最长长度// 根据当前中心i和最长长度计算起始位置start = i - (MAXLen - 1) / 2;}}// 根据最长回文的起始位置和长度,截取并返回结果return s.substr(start, MAXLen); }
};
解析:
1. 核心思路
采用中心扩展法求解最长回文子串,利用回文串的对称特性:
- 回文串可以以单个字符为中心(奇数长度,如 "aba")
- 或以两个相邻字符为中心(偶数长度,如 "abba")
- 遍历字符串中每个可能的中心,向两侧扩展寻找最长回文
2. 变量作用
Len1
:记录以当前字符为中心的奇数长度回文的最大长度Len2
:记录以当前字符和下一个字符为中心的偶数长度回文的最大长度MAXLen
:全局最长回文子串的长度(初始为 1,因为单个字符也是回文)start
:全局最长回文子串的起始索引
3. 关键逻辑
奇数长度回文处理:
- 从
i-1
和i+1
开始向两侧扩展 - 每次扩展成功(
s[l] == s[r]
)则更新长度Len1 = r-l+1
- 扩展失败则退出循环
- 从
偶数长度回文处理:
- 从
i
和i+1
开始向两侧扩展 - 每次扩展成功则更新长度
Len2 = r-l+1
- 扩展失败则退出循环
- 从
全局最大值更新:
- 计算当前中心能找到的最长回文长度
currentMax
- 若
currentMax
大于历史最大值MAXLen
,则更新:MAXLen = currentMax
(更新最长长度)start = i - (MAXLen - 1)/2
(通过中心和长度计算起始位置)
- 计算当前中心能找到的最长回文长度
4. 起始位置计算原理
公式 start = i - (MAXLen - 1)/2
的推导:
- 对于奇数长度(如长度 5,中心 i=2):
start = 2 - (5-1)/2 = 0
(正确对应 "abcba" 的起始索引 0) - 对于偶数长度(如长度 4,中心 i=1):
start = 1 - (4-1)/2 = 1-1=0
(正确对应 "abba" 的起始索引 0) - 利用整数除法自动处理奇偶数差异,无需额外判断
5. 复杂度分析
- 时间复杂度:O (n²),n 为字符串长度。每个中心最多扩展 O (n) 次,共 n 个中心
- 空间复杂度:O (1),仅使用常数个变量,无额外空间开销