LeetCode 5. 最长回文子串

5. 最长回文子串

难度 中等

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

示例 1:

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

示例 2:

输入:s = "cbbd"
输出:"bb"

提示:

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

题解

​ 这里需要解释两个概念,回文和子串。回文就是从左往右和从右往左读都是一样的,比如abcba。子串和子序列不一样,子串必须连续,子序列不一定连续。

​ 这道题是让我们找到最长的回文子串,当然暴力的方法肯定能找到,就是遍历+枚举,但是这应该会超时,就找别的方法去了,三年前看学过最长回文子串的算法,使用了动态规划,中心扩展法和马拉车法。

动态规划

​ 这道题怎么动态规划呢,如果s[i]和s[j]相同,且s[i + 1]到s[j - 1]是回文子串,那么s[i]到s[j]就是回文子串。那么我们动态规划方程就为dp[i] [j] = dp[i + 1] [j - 1] + 2。

​ 实现上面的动态规划,就需要一个辅助数组dp,但是我们并不是用上面的记录长度数组,而是换为布尔数组,如果dp[i] [j]为true的话,那么从i到j的子串是回文子串。

​ 动态规划四部曲:

  • 确定状态
    • 第一种,s[i] = s[j]
      • 如果dp[i + 1] [j - 1]为true,那么dp[i] [j] = true
      • 如果dp[i + 1] [j - 1]为false,那么dp[i] [j] = false
    • 第二种,s[i] != s[j],那么dp[i] [j] = false
  • 初始条件和边界情况

    • 如果长度为1,那肯定是回文子串,dp[i] [i] = true
    • 如果长度为2,如果s[i]和s[i + 1]相等的话,dp[i] [i + 1] = true
  • 状态转移方程

    • 长度大于3,如果dp[i + 1] [j - 1]为true,且s[i] = s[j],那么dp[i] [j] = true
  • 确定计算顺序

    • 枚举从长度3开始的子串
class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();//数组长度
        int start = 0;//最长回文子串开始下标
        int maxlength = 1;//一定能找到长度为1的子串
        boolean p[][] = new boolean[s.length()][s.length()];//辅助数组

        for(int i = 0 ; i < len; i++){//初始化
            p[i][i] = true;//长度为1的子串,一定是回文子串
            if(i < len - 1 && s.charAt(i) == s.charAt(i + 1)){//长度为2的子串,判断成功,更新状态
                p[i][i + 1] = true;
                start = i;
                maxlength = 2;
            }
        }

        for(int strlen = 3; strlen <= len; strlen++){//长度为3以上的数组
            for(int i = 0; i <= len - strlen; i++){//从下标为0开始遍历
                int j = i + strlen - 1;//长度strlen = j - i + 1,移项
                if(p[i + 1][j - 1] && s.charAt(i) == s.charAt(j)){//如果dp[i+1][j-1]为true,且s[i]=s[j],是回文子串,更新状态
                    p[i][j] = true;
                    maxlength = strlen;
                    start = i;
                }
            }
        }
        return s.substring(start, start + maxlength);
    }
}
中心扩展法

​ 中心扩展法就是从当前位置开始扩展,为什么从这个位置开始扩展呢,我觉得是因为中心扩展的话,只需要判断最新扩展的字符,而不像暴力法从一遍开始枚举,需要重复判断整个子串,这样时间效率上应该比较高。

​ 不过中心扩展法有点需要注意,就是回文子串长度是奇数还是偶数。

  • 奇数,就是中心点对称,开始坐标是一致的
  • 偶数,就是中心对称,开始坐标不一致
class Solution {
    public String longestPalindrome(String s) {
        if(s == null || s.length() < 1){//长度为0
            return "";
        }

        int start = 0;//记录最长回文子串的开始坐标
        int end = 0;//记录最长回文子串的结束坐标
        for(int i = 0; i < s.length(); i++){//从左到右枚举
            int len1 = expandAroundCenter(s, i, i);//最长回文子串是奇数
            int len2 = expandAroundCenter(s, i, i + 1);//最长回文子串是偶数
            int len = Math.max(len1, len2);//获取最长长度
            if(len > end - start){//如果当前最长回文子串长度大于之前的最长回文子串,更新状态
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end + 1);//返回从start到end+1的子串
    }

    public int expandAroundCenter(String s, int left, int right){
        while(left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)){//边界限制,扩展字符判断
            --left;
            ++right;
        }
        return right - left - 1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值