5. 最长回文子串

Posted by CodingWithAlice on August 26, 2023

5. 最长回文子串

力扣 15. 给你一个字符串 s,找到 s 中最长的回文子串。如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

示例1:

输入s = "babad"
输出"bab"
解释"aba" 同样是符合题意的答案

示例 2:

输入s = "cbbd"
输出"bb"
  • 解法一(自己写的)
    • 核心:在字符串循环中,先找到重复的字符位置,从最远位置开始,用指针往回看是否满足回文
    • 因为多次循环,所以设置了一些退出条件:回文判断由远到近,找到一个就不用再找了,因为不会再找到当前字符更长的子串

时间:464ms 内存:46.6M

var longestPalindrome = function(s) {
    // 边界条件
    if(!s || s.length < 2) {
        return s;
    }
    // 用于存储当前找到的最大长度的 str
    let currentStr = '';
    let currentMax = 0; // 当前最大长度
    const len = s.length;
    for(let i = 0; i < len; i++) {
        const item = s[i];
        const index = i;
        // 当前项为 item - 找到右侧和 item 相等的所有字符位置
        const itemEqualIndexs = [];
        let restIndex = index + 1;
        while(restIndex < len) {
            if(s[restIndex] === item) {
                // 字符位置由大到小排列
                itemEqualIndexs.unshift(restIndex);
            }
            restIndex++;
        }
        // 当前字符没有重复的 - 没有回文子串 && 且当前没有子串 -> 第一次执行
        if(!itemEqualIndexs.length && currentMax === 0) {
            currentMax = 1;
            currentStr = item;
        }
        // 利用指针遍历循环当前子串是否满足回文,不满足则继续使用下一个字符位置进行判断
        itemEqualIndexs.forEach((equal) => {
            let L = index;
            let R = equal;
            // 先判断当前 item 可以产生的字符串长度是否可能大于 currentMax
            if((R - L + 1) < currentMax) {
                return
            }
            while(s[L] === s[R] && L < R) {
                L++;
                R--;
            }
            // 代表符合回文数
            if(L >= R) {
                currentStr = s.slice(index, equal + 1);
                currentMax = currentStr.length;
                // 在当前字符,已经找到最长回文子串时,不去计算其他子串
                return
            }
        })
    }
    return currentStr;
};
  • 解法二:中心扩散法
    • 一开始看了好久,核心在于,传入 helper 的参数,i和i 或者 i和i+1,helper 函数中,是以这两点作为起点,去找满足回文的子串,这样就只要循环一次就能够找到最长子串了 -> 从中心点开始扩散寻找,效率高很多

时间:100ms 内存:41.6M

var longestPalindrome = function(s) {
        if (s.length<2){
            return s
        }
        let res = ''
        for (let i = 0; i < s.length; i++) {
            // 回文子串长度是奇数
            helper(i, i)
            // 回文子串长度是偶数
            helper(i, i + 1) 
        }

        function helper(m, n) {
            while (m >= 0 && n < s.length && s[m] == s[n]) {
                m--
                n++
            }
            // 注意此处m,n的值循环完后  是恰好不满足循环条件的时刻
            // 此时m到n的距离为n-m+1,但是mn两个边界不能取 所以应该取m+1到n-1的区间  长度是n-m-1
            if (n - m - 1 > res.length) {
                // slice也要取[m+1,n-1]这个区间 
                res = s.slice(m + 1, n)
            }
        }
        return res
};