一起刷LeetCode——最长回文子串(双指针)

369 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情

最长回文子串

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

来源:最长回文子串

分析

  • 对于呈中心对称的字符串称为回文串
  • 要求一个字符串的最长回文子串,有几种方法:

暴力解决

  • 暴力找出所有的子串判断是否是回文的,记录长度就可以找到最长的回文子串,但是太暴力,时间复杂度比较高,不优雅

双指针

  • 之前在问题无重复字符的最长子串中写过,字符串的问题比较推荐使用双指针,同样的这个题同样可以使用双指针
  • 回文串是左右对称的,长度可能是奇数,可能是偶数
  • 遍历字符串的时候,把当前的字符当成中心,向两边扩展,找到这个以这个字符为中心的长度为奇数的回文串和这个字符的下一个字符和当前字符相同,以这相邻的两个字符为中心的长度为偶数的回文串,维护一个最长的回文串就是题目的答案

代码

var longestPalindrome = function(s) {
    let longest = '';
    for (let i=0; i<s.length; i++){
        generatePalindrome(i, i);
        generatePalindrome(i, i+1);
    }
    function generatePalindrome(l, r){
        while (l>=0 && r<s.length && s[l]===s[r]){
            if (r-l+1 > longest.length){
                longest = s.slice(l, r+1);
            }
            l--;
            r++;
        }
    }
    return longest;
};

动态规划

  • 用动态规划的思想需要对寻找这个最长回文子串问题进行分解,分解成相同的子问题,子问题就是寻找子串,判断是否是回文的,找到最长的回文子串,问题游刃而解:
  • 状态定义:dp[i][j]表示从i到j的子串是否是回文
  • 状态转移:
    • 长度为1的回文子串:当i=j的时候,dp[i][i]=true
    • 长度为2的回文子串:当s[i]=s[i+1]的时候,dp[i][i+1]=true
    • 长度大于等于3的回文子串:当s[i]=s[j]的时候,dp[i][j]=s[i+1][j-1]

代码

var longestPalindrome = function(s) {
    if(s.length <= 1) return s;
    const dp = [...new Array(s.length + 1)].map(_ => new Array(s.length + 1).fill(false));
    let longest = '';
    for(let i = 0; i < s.length; i++) {
        dp[i][i] = true;
        longest = s[i];
    }
    for(let i = 0; i < s.length; i++) {
        if(s[i] === s[i + 1]) dp[i][i+1] = true;
        if(dp[i][i+1]) longest = s.substring(i, i + 2);
    }
    for(let i = s.length - 1; i >= 0; i--) {
        for(let j = i + 2; j < s.length; j++) {
            dp[i][j] = dp[i+1][j-1] && s[i] === s[j];
            if(dp[i][j]) longest = longest.length < (j - i + 1) ? s.substring(i, j + 1) : longest;
        }
    }
    return longest;
}

总结

  • 最近发现很多特殊的字符串问题写着有些生疏了,最近就补一补字符串相关的问题吧
  • 动态规划只要定义好了状态和状态转移,代码输出会相对容易
  • 双指针在解决字符串问题的时候,个人觉得是好用的
  • 今天也是有收获的一天