算法题笔记--最长回文子串

157 阅读1分钟

leetcode原题:

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

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

示例 2:
输入:s = "cbbd"
输出:"bb"
 

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

在题目的事例里可以看出来需要考虑两种情况:奇数长度回文子串和偶数长度回文子串,奇数长度,中间的字母不用管,两侧的各自相等;偶数长度,没有中间的字母,两侧的各自相等。

针对每个字母,作为奇数长度的回文串的中间的字母,进行扩散检查;针对相邻的两个字母,作为偶数长度的回文串,进行扩散检查;


奇数:假设现在都字符串 sbabadc,那么最长回文子串为 aba,当我的 i 遍历到 b 位置时以i 为中心向两边扩散去对比两边的字符串是否相等;

image.png

偶数:假设现在都字符串 sbabbadc,那么最长回文子串为 abba,当我的 i 遍历到 b 位置时以ii+1 为中心两边扩散去对比两边的字符串是否相等;

image.png

通过上面的分析我们可以封装一个扩展函数,在这个扩展函数里我们要做的就是去对比以 i 为中心的两边相等的回文子串并返回他的长度,需要三个参数,一个就是我们需要遍历的字符串 s,还有我们需要对比的两个字符的位置我这里命名为 startend,满足我们遍历的条件是,开始位置大于等于0,结束位置小于等于字符串的长度,也就是两个变量一旦到达了字符串的边界位置就可以停止对比了,其次最重要的就是两边的字符相等,满足条件后我们就执行 end++ start--;

let expand = function (s,start,end) {
    while(start>=0 && end<s.length && s.charAt(start)==s.charAt(end)){
        end++
        start--
    }
    return end-start-1
}

结束后我返回的是这个回文字符串的长度,为什么是字符串的长度而不是这个字符串呢,首先我们我们当前返回的只是一个回文字符串并不是我们要的最长的回文字符串,所以在这里去截取字符串会比较消耗内存,影响代码的执行效率;

还有一点就是return的值,如下图,我们结束的时候 startend 的位置如图所示,所以我们返回的长度不能是 end-start+1, 而应该用 end 的前一位下标去减 start 的后一位下标,也就是. end-1-(start+1)+1,也就是 end-start-1;

image.png


封装了扩展工具函数,我们只需要调用他并传入相应的值就可以,当是奇数个数时我们的中心就是下标为 i 的字符,当时偶数个数时我们的中心就是下标为 ii+1 的字符,只要我们在遍历时同时去考虑这两种情况即可,然后对比取最大的长度,再去和我们全局的最大长度 maxLen 对比,截取最大长度的回文字串并返回结果 result

var longestPalindrome = function(s) {
    let maxLen = 0;  // 最大长度
    let result = '';  // 结果返回的最大长度的回文字符串
    for(let i=0; i<s.length; i++){
        const len1 = expand(s,i,i)   // 奇数个数
        const len2 = expand(s,i,i+1)  // 偶数个数
        const len = Math.max(len1,len2)  // 取最大长度
        if(len>maxLen){  
            maxLen=len
            const start = i - Math.floor((len-1)/2)
            const end = i + Math.floor(len/2)
            result = s.substring(start,end+1)   // 截取字符串
        }
    }
    return result
};

完整代码如下:

/**
 * @param {string} s
 * @return {string}
 */
var longestPalindrome = function(s) {
    let maxLen = 0;
    let result = '';
    for(let i=0; i<s.length; i++){
        const len1 = expand(s,i,i)
        const len2 = expand(s,i,i+1)
        const len = Math.max(len1,len2)
        if(len>maxLen){
            maxLen=len
            const start = i - Math.floor((len-1)/2)
            const end = i + Math.floor(len/2)
            result = s.substring(start,end+1)
        }
    }
    return result
};

let expand = function (s,start,end) {
    while(start>=0 && end<s.length && s.charAt(start)==s.charAt(end)){
        end++
        start--
    }
    return end-start-1
}

依旧还是有进步空间,继续加油!

image.png