leetcode(5.最长回文子串)【中等】

一、题目

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

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

示例 2:

输入:s = “cbbd”
输出:“bb”
提示:

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

二、题解

对于一个子串而言,如果它是回文串,并且长度大于2,那么将它首尾的两个字母去除之后,它仍然是个回文串。例如对于字符串
“ababa”,如果我们已经知道“bab” 是回文串,那么“ababa” 一定是回文串,这是因为它的首尾两个字母都是 “a”。

根据这样的思路,我们就可以用动态规划的方法解决本题。我们用 P(i,j)表示字符串s的第i到j个字母组成的串(下文表示成 s[i:j])是否为回文串:
P ( i , j ) = { t r u e , 如果子串  S i … S j 是回文串 f a l s e , 其它情况 P(i,j)=\begin{cases}\mathrm{true},\quad&\text{如果子串 }S_i\ldots S_j\text{是回文串}\\\mathrm{false},\quad&\text{其它情况}\end{cases} P(i,j)={true,false,如果子串 SiSj是回文串其它情况

这里的「其它情况」包含两种可能性:
∙ s [ i , j ]  本身不是一个回文串 ; ∙ i > j ,此时  s [ i , j ]  本身不合法。 \begin{aligned}\\ \bullet&s[i,j]\text{ 本身不是一个回文串}; \\\bullet&i>j\text{,此时 }s[i,j]\text{ 本身不合法。} \end{aligned} s[i,j] 本身不是一个回文串;i>j,此时 s[i,j] 本身不合法。
那么我们就可以写出动态规划的状态转移方程:
P ( i , j ) = P ( i + 1 , j − 1 ) ∧ ( S i = = S j ) P(i,j)=P(i+1,j-1)\wedge(S_i==S_j) P(i,j)=P(i+1,j1)(Si==Sj)
也就是说,只有 s[i+1:j−1]是回文串,并且s的第i和j个字母相同时,s[i:j]才会是回文串。
以上的所有讨论是建立在子串长度大于2的前提之上的

考虑动态规划中的边界条件:
子串的长度为 1 或 2。

  • 对于长度为1的子串,它显然是个回文串;
  • 对于长度为2的子串,只要它的两个字母相同,它就是一个回文串。

因此我们就可以写出动态规划的边界条件:
{ P ( i , i ) = true P ( i , i + 1 ) = ( S i = = S i + 1 ) \begin{cases}P(i,i)=\text{true}\\P(i,i+1)=(S_i==S_{i+1})\end{cases} {P(i,i)=trueP(i,i+1)=(Si==Si+1)
根据这个思路,我们就可以完成动态规划了,最终的答案即为所有 P ( i , j ) = true 中  j − i + 1 P(i,j)=\text{true 中 }j-i+1 P(i,j)=true  ji+1 (即子串长度)的最大值。
注意:
在状态转移方程中,我们是从长度较短的字符串向长度较长的字符串进行转移的,因此一定要注意动态规划的循环顺序。

  1. 若为1个字符的字符串,直接返回,肯定是
  2. 创建dp[][]用来存储是否是回文子串
    1. 首先,动态规划最小单位,每一个字符构成一个回文子串
    2. 然后逐步向上扩展
      1. 起始,长度确定子串
      2. 如果子串两端不同,false
        如果相同:
        1. 是 1或2 个字符直接标记为true
        2. 不是2个字符,布尔值等于左右各缩进一个
      3. maxlen记录长度,如果子串长度大于maxlen,则更新
      4. 回文不一定是从头开始的随时更新起始位置
      5. 考虑越界,左边界越界、长度越界

三、代码

public String longestPalindrome(String s) {
        int len = s.length();
        // 只有一个字符的情况
        if(len < 2){
            return s;
        }
        // 动态规划
        int maxLen = 1, begin = 0;
        boolean[][] dp = new boolean[len][len]; // 用来表示是否为回文
        // 初始化:所有长度为 1 的子串都是回文串
        for (int i = 0; i < len; i++) {
            dp[i][i] = true;
        }
        char[] c = s.toCharArray();
        for(int l = 2; l <= len; l++){ //子串长度
            for(int i = 0; i < len-2; i++){ // 子串起始位置 i
                int j = i+l-1; // 子串结束位置 j
                // 边界越界
                if(j>len-1){
                    break;
                }
                if (c[i] != c[j]) {
                    dp[i][j] = false;
                } else {
                    if (j - i < 3) {  // 不用再缩进
                        dp[i][j] = true;
                    } else {  // 缩进继续判断
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }
                // 向外上合并停止的时刻
                if (dp[i][j] && j - i + 1 > maxLen) {
                    maxLen = j - i + 1;
                    begin = i;
                }
            }
        }
        return s.substring(begin,begin+maxLen);
    }

四、结果

在这里插入图片描述

五、说明

  本文章仅用于记录个人做题记录
  由于本人是个小菜鸡(实锤),题目解法并非最优,且解题过程中参考(抄袭)各大佬解题方法,望见谅。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值