[LeetCode05题 - 最长回文字符串] | 刷题打卡

243 阅读3分钟

题目描述

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

示例 1:

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

示例 2:

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

示例 3:

输入:s = "a"
输出:"a"

示例 4:

输入:s = "ac"
输出:"a"

提示

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母(大写和/或小写)组成

来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/lo…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

这题最简单的思路应该就是 暴力遍历 ,取出全部子串,判断每个子串是否是回文子串。当是回文子串的时候,取出长度最长的回文子串。

class Solution {
    String result = "";

    public String longestPalindrome(String s) {
        for (int i = 0; i < s.length(); i++) {
            for (int j = i + 1; j <= s.length(); j++) {
                String temp = s.substring(i, j);
                if (isPaindrome(temp)) {
                    result = result.length() > temp.length() ? result : temp;
                }
            }
        }
        return result;
    }

    private boolean isPaindrome(String temp) {
        StringBuilder sb = new StringBuilder(temp);
        return temp.equals(sb.reverse().toString());
    }
}

这种解法的时间复杂度是 O(n2) ,但是我提交之后提示时间超出限制。终究是效率敌不过。


第二种思路就是我遍历到每个元素的时候,都去获取当前元素的左右两边是否对称。一直匹配到不对称,这个子串就是一个回文子串,也就是对于每个元素,我都至少能够拿到一个长度为 1 的回文子串。但是这种情况要注意一个问题,如果回文子串的长度是 奇数 ,那么当前元素一定是中间元素,但是如果是 偶数 ,那么当前元素有可能是中间元素的靠左,也有可能是中间元素靠右。我们定义两个 游标 left , right 分别表示当前元素的左右两个元素。

public String longestPalindrome(String s) {

        if (s == null || s.length() == 0) {
            return "";
        }
        int strLen = s.length();
        int left = 0;
        int right = 0;
        int len = 1;
        int maxStart = 0;
        int maxLen = 0;

        for (int i = 0; i < strLen; i++) {
            left = i - 1;
            right = i + 1;
            while (left >= 0 && s.charAt(left) == s.charAt(i)) {
                len++;
                left--;
            }
            while (right < strLen && s.charAt(right) == s.charAt(i)) {
                len++;
                right++;
            }
            while (left >= 0 && right < strLen && s.charAt(right) == s.charAt(left)) {
                len = len + 2;
                left--;
                right++;
            }
            if (len > maxLen) {
                maxLen = len;
                maxStart = left;
            }
            len = 1;
        }
        return s.substring(maxStart + 1, maxStart + maxLen + 1);

    }

这种解法仍然存在许多重复计算。我们可以进一步考虑动态规划的做法,动态规划大部分的思路都是增加一个备忘录来维护每次计算结果。

我们用一个 boolean dp[l][r] 表示字符串从 ij 这段是否为回文。试想如果 dp[l][r]=true ,我们要判断 dp[l-1][r+1] 是否为回文。只需要判断字符串在 (l-1)(r+1) 两个位置是否为相同的字符。

最终代码

public String longestPalindrome(String s) {
        if (s == null || s.length() < 2) {
            return s;
        }
        int strLen = s.length();
        int maxStart = 0;  //最长回文串的起点
        int maxEnd = 0;    //最长回文串的终点
        int maxLen = 1;  //最长回文串的长度

        boolean[][] dp = new boolean[strLen][strLen];

        for (int r = 1; r < strLen; r++) {
            for (int l = 0; l < r; l++) {
                if (s.charAt(l) == s.charAt(r) && (r - l <= 2 || dp[l + 1][r - 1])) {
                    dp[l][r] = true;
                    if (r - l + 1 > maxLen) {
                        maxLen = r - l + 1;
                        maxStart = l;
                        maxEnd = r;

                    }
                }

            }

        }
        return s.substring(maxStart, maxEnd + 1);

    }

作者:reedfan
链接:leetcode-cn.com/problems/lo…

结语

本题有多种思路,前两种很容易想到,动态规划的做法我参考了一下其他 coder 的题解,感觉本题最主要的还是要理解初始状态和状态转移条件。本题的初始状态是:l=r 时,此时 dp[l][r]=true 。状态转移方程为: dp[l][r]=true 并且 (l-1)(r+1) 两个位置为相同的字符,此时 dp[l-1][r+1]=true

本文正在参与「掘金 2021 4 月刷题打卡」, 点击查看 活动详情。喜欢的话点个赞吧。