与动态规划的爱恨情仇——最长回文子串

85 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第23天,点击查看活动详情


最近一直在力扣刷题,也逐渐对各类题型有了自己的理解,所谓见招拆招,将自己的浅显经验分享一下,帮助更多在编程路上的朋友们。


最长回文子串

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

示例 1:

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

示例 2:

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

提示:

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

思路

使用动态规划并不是最优的解法,时间复杂度还是很高,但比较容易理解。

对于字符串"baab"来说,如果"aa"是回文子串,其两侧的字符相同,则加上两侧的字符后也是回文子串。

定义dp[i][j]为从位置 i 到 位置 j 的子串是否为回文子串。状态转移方程也很容易想到:

dp[i][j] = dp[i + 1][j - 1] && s.charAt(i) == s.charAt(j)

单个字符即为回文子串,如果两个相邻字符相同,也属于回文子串,我们需要不停的遍历整个字符串,确保能获得最长的回文子串。

在遍历时需要根据长度来遍历,如果根据位置从前往后遍历,会漏掉很多情况,因为dp[i][j]的定义就是根据长度来定义的,长度由小到大才能考虑到每一种情况。

题解

class Solution {
    public String longestPalindrome(String s) {
        int n = s.length();
        char[] c = s.toCharArray();
        int start = 0, end = 0;
        boolean[][] dp = new boolean[n][n];
        for(int i = 0; i < n; i++) {
            dp[i][i] = true;
        }
        for(int len = 2; len <= n; len++) {
            for(int left = 0; left + len <= n; left++) {
                int right = left + len - 1;
                if(len == 2 && c[left] == c[right]) {
                    dp[left][right] = true;
                }else if(dp[left + 1][right - 1] && c[left] == c[right]) {
                    dp[left][right] = true;
                }
                if(dp[left][right]) {
                    start = left;
                    end = right;
                }
            }
        }
        return s.substring(start, end + 1);
    }
}