LeetCode5 :最长回文子串

题目:

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

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串

示例 1:

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

示例 2:

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

思路:

  • dp[i][j] 表示从字符数组的下标i 开始到下标j 的子序列 s[i,i+1,..j] 是否是回文串
  • 从数组下标 left到right的子序列,跟从left + 1 到 right - 1的子序列,要么都是回文串,要么都不是回文串。
  • 可以枚举子序列的长度subLength ,以及左下标left ,由 subLength 和 left 可以确定右边界

易错点:

  • //注意,长度低于2的直接就是回文子串了
  • //注意,子串长度subLength是从2开始的,并且要小于等于 length
  • //注意,由 子串长度subLength 和 左下标 left 可以确定右边界,即 right - left + 1 = subLength
  • //注意:substring不要写错了,语法是左开右闭,注意边界问题
  • //注意:超出索引就跳过,要小心数组越界的问题

代码:

    /**
     * 对于一个子串而言,如果它是回文串,并且长度大于 2,那么将它首尾的两个字母去除之后,它仍然是个回文串。
     *
     * P(i,j) 表示字符串 s 的第 i 到 j 个字母组成的串。(s[i:j] 类似)
     *
     * (1)状态转移方程:
     *
     * P(i,j)=P(i+1,j−1)∧(Si==Sj)
     * 也就是:只有 s[i+1:j−1] 是回文串,并且 s 的第 i 和 j 个字母相同时,s[i:j] 才会是回文串。
     *
     * (2)初始值:
     * 对于长度为 1 的子串,它显然是个回文串;
     * 也就是 P(i,i)=true;
     *
     * 对于长度为 2 的子串,只要它的两个字母相同,它就是一个回文串。因此我们就可以写出动态规划的边界条件:
     * 也就是P(i,i+1)=  (Si==Si+1)
     *
     * 详情见: https://leetcode.cn/problems/longest-palindromic-substring/solution/zui-chang-hui-wen-zi-chuan-by-leetcode-solution/
     *
     * @param s
     * @return
     */
    public String longestPalindrome(String s) {
        int length = s.length();
        //注意,长度低于2的直接就是回文子串了
        if (length < 2) {
            return s;
        }
        int maxLength = 1;
        int begin = 0;

        // dp[i][j] 表示从字符数组的下标i开始到下标j的子序列 s[i,i+1,..j] 是否是回文串
        boolean[][] dp = new boolean[length][length];
        // 初始化:所有长度为 1 的子串都是回文串
        for (int i = 0; i < length; i++) {
            dp[i][i] = true;
        }

        char[] charArray = s.toCharArray();
        // 递推开始
        // 先枚举子序列的长度
        //注意,子串长度subLength是从2开始的,并且要小于等于 length
        for (int subLength = 2; subLength <= length; subLength++) {
            // 枚举左边界,左边界的上限设置可以宽松一些
            for (int left = 0; left < length; left++) {
          //注意,由 子串长度subLength 和 左下标 left 可以确定右边界,即 right - left + 1 = subLength 
                int right = subLength + left - 1;
                // 如果右边界越界,就可以退出当前循环
                //注意:超出索引就跳过,要小心数组越界的问题
                if (right >= length) {
                    break;
                }

                if (charArray[left] != charArray[right]) {
                    //子序列的左边界和右边界的值,如果不相同,就不可能是回文串
                    dp[left][right] = false;
                } else {
                    //左边界和右边界相同时,子序列的长度为1或为2,都是回文串。
                    if (right - left < 3) {
                        dp[left][right] = true;
                    } else {
                        //状态转移方程,左边界和右边界相同时,
                        // 从left到right的子序列,跟从left + 1 到 right - 1的子序列,要么都是回文串,要么都不是回文串。
                        dp[left][right] = dp[left + 1][right - 1];
                    }
                }

                // 只要 dp[left][subLength] == true 成立,就表示子串 s[left..subLength] 是回文,此时记录回文长度和起始位置
                if (dp[left][right] && right - left + 1 > maxLength) {
                    //找出最大子序列的长度
                    maxLength = right - left + 1;
                    begin = left;
                }
            }
        }
        //注意:substring不要写错了,语法是左开右闭,注意边界问题
        return s.substring(begin, begin + maxLength);
    }


posted on   乐之者v  阅读(4)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
历史上的今天:
2022-01-26 Dubbo使用Rest开发服务
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示