题目描述
给你一个字符串 s,找到 s 中最长的 回文 子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”
提示:
- 1 <= s.length <= 1000
- s 仅由数字和英文字母组成
思考一:暴力解法
从最长可能的子串长度开始,依次检查所有可能的子串是否为回文,一旦找到第一个回文子串就直接返回(因为是从最长开始检查的)。
是的,这段代码属于暴力解法,其核心思路是:从最长可能的子串长度开始,依次检查所有可能的子串是否为回文,一旦找到第一个回文子串就直接返回(因为是从最长开始检查的)。
时空复杂度分析
-
时间复杂度:O(n³)
- 外层循环:遍历所有可能的子串长度,从n到2,共O(n)次
- 中层循环:对每个长度,遍历所有可能的起始位置,共O(n)次
- 内层检查:
isPalindrome函数通过双指针比较子串是否为回文,最坏情况下需要O(n)次比较 - 三者相乘,总时间复杂度为O(n × n × n) = O(n³)
-
空间复杂度:O(1)
- 仅使用了常数个变量(
maxLen、start、i、j等)存储中间状态 - 没有使用与输入规模相关的额外数据结构(如数组、哈希表等)
- 因此空间复杂度为常数级O(1)
- 仅使用了常数个变量(
代码
/**
* @param {string} s
* @return {string}
*/
var longestPalindrome = function(s) {
const n = s.length;
if (n <= 1) return s; // 边界情况处理
let maxLen = 1;
let start = 0; // 记录最长回文子串的起始索引,避免频繁截取字符串
// 从最长可能的子串长度开始检查
for (let len = n; len > 1; len--) {
// 检查所有长度为len的子串
for (let i = 0; i <= n - len; i++) {
let j = i + len - 1; // 子串结束索引
if (isPalindrome(s, i, j)) {
// 找到最长的回文子串,直接返回
return s.substring(i, j + 1);
}
}
}
// 如果没有找到更长的回文子串,返回第一个字符
return s[0];
};
// 优化:直接检查原字符串的指定范围,避免创建子串
function isPalindrome(s, l, r) {
while (l < r) {
if (s[l] !== s[r]) return false;
l++;
r--;
}
return true;
}
思考二:中心扩展法
采用中心扩展法的思路:回文子串具有中心对称的特性,可通过遍历每个可能的中心(包括单个字符和两个相同字符),向两侧扩展检查最长的回文子串。这种方法避免了暴力枚举所有子串的高时间开销,更高效地找到最长回文子串。
算法过程
- 边界处理:若字符串长度≤1,直接返回原字符串
- 初始化变量:
maxLen记录最长回文子串长度(初始为1),start记录其起始索引 - 遍历每个可能的中心:
- 对每个索引
i,以i为中心(奇数长度回文)扩展检查 - 对每个索引
i,以i和i+1为中心(偶数长度回文)扩展检查
- 对每个索引
- 扩展检查:
- 从中心向两侧延伸,比较左右字符是否相等
- 若相等则继续扩展,否则停止
- 记录扩展过程中找到的最长回文子串的长度和起始索引
- 返回结果:根据最长回文子串的起始索引和长度截取并返回
时空复杂度分析
- 时间复杂度:O(n²),其中n为字符串长度。每个中心最多扩展O(n)次,共有O(n)个中心
- 空间复杂度:O(1),仅使用常数级额外空间
代码
/**
* @param {string} s
* @return {string}
*/
var longestPalindrome = function(s) {
const n = s.length;
if (n <= 1) return s;
let start = 0, maxLen = 1;
// 中心扩展函数
const expandAroundCenter = (left, right) => {
while (left >= 0 && right < n && s[left] === s[right]) {
// 计算当前回文子串长度
const currentLen = right - left + 1;
// 更新最长回文子串信息
if (currentLen > maxLen) {
maxLen = currentLen;
start = left;
}
left--;
right++;
}
};
// 遍历每个可能的中心
for (let i = 0; i < n; i++) {
// 奇数长度回文(中心为单个字符)
expandAroundCenter(i, i);
// 偶数长度回文(中心为两个相同字符)
expandAroundCenter(i, i + 1);
}
return s.substring(start, start + maxLen);
};
338

被折叠的 条评论
为什么被折叠?



