leetCode 第五题 最长回文子串 js中心扩展法详解

2,285 阅读3分钟

leetCode 第五题 最长回文子串

题目描述

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

这道题有很多种解法,但暴力法无疑时间复杂度是最高的,会超出时间限制,这里就介绍一下中心扩展法

中心扩展法

所谓中心扩展法,就是从一个中心往两边扩展判断

给定字符串s,下标i,根据中心扩展法,我们需要往两边扩展判断

left = i - 1;right = i + 1
如果s[left]等于s[right]则继续往两边扩展判断
当s[left]不等于s[right]时结束,这时候返回right-left就是回文串的长度

贴上代码

function search(s,l,r){
    let left = l,
        right = r;
    while(left >= 0 && right < s.length && s[left] === s[right]){
        left--;
        right++;
    }
    return right - left - 1; //right - left - 2 + 1 减二是因为上一个判断循环了一次left-- 和 right++
}

由于字符串aba和abba都是回文字符串,所以这两种类型全都要判定,我们给left,right的赋值就会不同 给定字符串s,下标i,第一种回文字符串aba使left=right=i,第二种abba使left=i,right=i+1 所以每个下标要使用两次中心扩展法。

贴上代码

for(let i = 0 ; i < s.length; i++){
    let len1 = search(s,i,i)
    let len2 = search(s,i,i+1)
}

通过上面的代码,我们已经找出了回文串的长度,那么接下来就是怎么得到它了。我们要得出所有回文子串中最长的那一个,为了后面方便得到回文子串,定义最长回文子串的开始下标start和结束下标end,最长回文子串就是s.slice(start,end+1) 那么代码应该长这样

贴上代码

var start = end = 0
for(let i = 0 ; i < s.length; i++){
    let len1 = search(s,i,i)
    let len2 = search(s,i,i+1)
    
    let len = Math.max(len1,len2)
    if(len > end -start){
        start = i - Math.floor((len - 1) / 2)
        end = i + Math.floor(len / 2)
    }   
}
return s.slice(start,end+1) 

这里有必要说start = i - Math.floor((len - 1) / 2);end = i + Math.floor(len / 2)是怎么得到的

  1. 假设坐标为i,当length为奇数时,i为中心点,取子字符串

    起始坐标 i_start = i - (len-1)/2

    终止坐标 i_end = i + (len-1)/2

  2. 假设坐标为i,当length为偶数时,i为中心前置点,取子字符串

    起始坐标 i_start = i - (len)/2 - 1

    终止坐标 i_end = i + (len)/2

那么整理一下就是

i_start = i - (len-1)/2          ------------ length为奇数
i_start = i - (len)/2 - 1        ------------ length为偶数

换算一下就是

i_start = i - (len-1)/2   ==  i - Math.floor((len-1)/2)                      ------------ length为奇数
i_start = i - (len)/2 - 1 ==  i - (len-2)/2   ==  i - Math.floor((len-1)/2)  ------------ length为偶数

同理

i_end = i + (len-1)/2  ==  i + Math.floor(len / 2)      ------------ length为奇数
i_end = i + (len)/2    ==  i + Math.floor(len / 2)      ------------ length为偶数

所以给定i对于所有length的回文子串响应的起始和终止坐标

start = i - Math.floor((len - 1) / 2)
end = i + Math.floor(len / 2)

贴上所有代码

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

    function search(s,l,r){
        let left = l,
            right = r;
        while(left >= 0 && right < s.length && s[left] === s[right]){
            left--;
            right++;
        }
        return right - left - 1;
    }
};