【LeetCode 热题100道笔记】 最长回文子串

题目描述

给你一个字符串 s,找到 s 中最长的 回文 子串。

示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。

示例 2:
输入:s = “cbbd”
输出:“bb”

提示:

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母组成

思考一:暴力解法

从最长可能的子串长度开始,依次检查所有可能的子串是否为回文,一旦找到第一个回文子串就直接返回(因为是从最长开始检查的)。

是的,这段代码属于暴力解法,其核心思路是:从最长可能的子串长度开始,依次检查所有可能的子串是否为回文,一旦找到第一个回文子串就直接返回(因为是从最长开始检查的)。

时空复杂度分析

  1. 时间复杂度:O(n³)

    • 外层循环:遍历所有可能的子串长度,从n到2,共O(n)次
    • 中层循环:对每个长度,遍历所有可能的起始位置,共O(n)次
    • 内层检查:isPalindrome函数通过双指针比较子串是否为回文,最坏情况下需要O(n)次比较
    • 三者相乘,总时间复杂度为O(n × n × n) = O(n³)
  2. 空间复杂度:O(1)

    • 仅使用了常数个变量(maxLenstartij等)存储中间状态
    • 没有使用与输入规模相关的额外数据结构(如数组、哈希表等)
    • 因此空间复杂度为常数级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. 边界处理:若字符串长度≤1,直接返回原字符串
  2. 初始化变量maxLen记录最长回文子串长度(初始为1),start记录其起始索引
  3. 遍历每个可能的中心
    • 对每个索引i,以i为中心(奇数长度回文)扩展检查
    • 对每个索引i,以ii+1为中心(偶数长度回文)扩展检查
  4. 扩展检查
    • 从中心向两侧延伸,比较左右字符是否相等
    • 若相等则继续扩展,否则停止
    • 记录扩展过程中找到的最长回文子串的长度和起始索引
  5. 返回结果:根据最长回文子串的起始索引和长度截取并返回

时空复杂度分析

  • 时间复杂度: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);
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值