美文网首页
0005. 最长回文子串

0005. 最长回文子串

作者: 蓝笔头 | 来源:发表于2021-08-12 11:29 被阅读0次

    题目地址

    https://leetcode-cn.com/problems/longest-palindromic-substring/

    题目描述

    给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
    
    示例 1:
    输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。 示例 2:
    输入: "cbbd" 输出: "bb"
    

    题解

    暴力枚举

    class Solution {
        public String longestPalindrome(String s) {
            int length = s.length();
            int resultBegin = 0;
            int maxLength = 0;
            for (int begin = 0; begin < length; ++ begin) {
                for (int end = length - 1; end >= begin; -- end) {
                    if (isPalindrome(s, begin, end)) {
    
                        // 保存最长的回文子串
                        int newLength = end - begin + 1;
                        if (newLength > maxLength) {
                            maxLength = newLength;
                            resultBegin = begin;
                        }
                        break;
                    }
                }
            }
    
            return s.substring(resultBegin, resultBegin + maxLength);
        }
    
        public boolean isPalindrome(String s, int begin, int end) {
            int length = (end - begin + 1) / 2;
            for (int i = 0; i < length; ++ i) {
                if (s.charAt(begin + i) != s.charAt(end - i)) {
                    return false;
                }
            }
    
            return true;
        }
    }
    

    时间复杂度 O(n^3),n 为字符串长度。

    上面枚举子串 [begin, end] 的操作是必需的。

    可以看看怎么优化 isPalindrome() 的逻辑,可以在常量时间判断子串 [begin, end] 是否是一个回文串。

    动态规划

    解题之前先来思考一个问题:如何判断一个子串 [begin, end] 是否是一个回文串?

    // case 1
    // end == begin,只有一个字符的情况下
    // [begin, end] 是回文串
    
    // case 2
    // end = begin + 1,只有两个字符的情况下,两个字符相同
    // 即 s.charAt(begin) == s.charAt(end)
    // [begin, end] 是回文串
    
    // case 3
    // [begin+1, end-1] 是回文串的情况下,s.charAt(begin) == s.charAt(end)
    // 如 aba 是回文串,xabax 也是回文串
    // [begin, end] 是回文串
    

    我们用 dp[begin][end] 表示 [begin, end] 子串是否是回文串,为 true 表示是回文串,为 false 表示不是回文串

    把上面的三个 case 用代码描述为:

    boolean dp[][] = new boolean[length][length];
    
    // 因为要从 begin+1 推导 begin 的值,所以要从大到小遍历
    for (int begin = length - 1; begin >= 0; --begin) {
        // 因为要从 end-1 推导 end 的值,所以要从小到大遍历
        for (int end = begin; end < length; ++end) {
            // case 1
            if (end == begin) {
                dp[begin][end] = true;
            }
    
            // case 2
            if (end == begin + 1 && s.charAt(begin) == s.charAt(end)) {
                dp[begin][end] = true;
            }
    
            // case 3
            if (dp[begin+1][end-1] && s.charAt(begin) == s.charAt(end)) {
                dp[begin][end] = true;
            }
        }
    }
    

    构建上述 dp 数组的时间复杂度为 O(n^2),n 为字符串长度。

    通过 dp 数组,我们可以在 O(1) 的时间复杂度中判断 [begin, end] 子串是否是回文串。

    我们还可以在构建 dp 数组的同时得到最长的回文子串,完整的代码如下所示:

    class Solution {
        public String longestPalindrome(String s) {
            int length = s.length();
            int resultBegin = 0;
            int maxLength = 0;
    
            boolean dp[][] = new boolean[length][length];
            // 因为要从 begin+1 推导 begin 的值,所以要从大到小遍历
            for (int begin = length - 1; begin >= 0; --begin) {
                // 因为要从 end-1 推导 end 的值,所以要从小到大遍历
                for (int end = begin; end < length; ++end) {
                    // case 1
                    if (end == begin) {
                        dp[begin][end] = true;
                    }
    
                    // case 2
                    if (end == begin + 1 && s.charAt(begin) == s.charAt(end)) {
                        dp[begin][end] = true;
                    }
    
                    // case 3
                    // 需要判断数组索引下标是否在可以访问的范围内
                    boolean inBounds = begin+1 < length && end-1 > 0;
                    if (inBounds && dp[begin+1][end-1] && s.charAt(begin) == s.charAt(end)) {
                        dp[begin][end] = true;
                    }
    
                    // 保存最长的回文子串
                    if (dp[begin][end]) {
                        int newLength = end - begin + 1;
                        if (newLength > maxLength) {
                            maxLength = newLength;
                            resultBegin = begin;
                        }
                    }
                }
            }
    
            return s.substring(resultBegin, resultBegin + maxLength);
        }
    }
    

    时间复杂度为:O(n^2),n 为字符串的长度。
    空间复杂度为:O(n^2),n 为字符串的长度。

    上述优化思路和 面试题 17.23. 最大黑方阵 的优化思路是一样的。
    通过预处理,我们缩减其中某一个步骤的时间复杂度,从而使整体的时间复杂度下降一个 O(N)

    相关文章

      网友评论

          本文标题:0005. 最长回文子串

          本文链接:https://www.haomeiwen.com/subject/ttocbltx.html