5. 最长回文子串

301 阅读1分钟

题目描述

image.png

方法一:动态规划 O(N2) O(N2)

思路

  • boolean dp[i][j]表示字符串s[i,j]是不是回文串 状态转移方程的思考
  • 如果dp[i+1][j-1]true,并且s[i]=s[j],则dp[i][j]也为true
  • 下一步,需要考虑边界情况,i+1 <= j-1必须成立,即 j-i >= 2j-i < 2的情况为 s[i,j]为单个字符或者为两个字符。
  • 所以得出状态转移方程:dp[i][j] = (s[i]=s[j]) && (dp[i+1][j-1] || j - i <= 1) 或者画图理解状态转移方程

class Solution {
    public String longestPalindrome(String s) {
        int l = s.length();
        boolean[][] dp = new boolean[l][l];
        int max = 1, start = 0;
        // 由于是参考左下方,所以需要从上到下填写(i)
        for (int j = 0; j < l; j++) {
            for (int i = 0; i < j + 1; i++) { // 需要覆盖 i = j, 单个元素
                if (s.charAt(i) != s.charAt(j)) {
                    dp[i][j] = false;
                } else {
                    if (j - i <= 1) {//没有左下方参考的格子,一个字符或两个字符
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i + 1][j - 1];//当前字符串至少有三个字符,可以两头减,参考左下方的格子
                    }
                    //确定完dp[i][j]为回文了,计算长度
                    if (dp[i][j] && j - i + 1 > max) {
                        max = j - i + 1;
                        start = i;
                    }
                }
            }
        }
        return s.substring(start, start + max);
    }
}

方法二:中心扩展 O(N2) O(1)

和暴力枚举判断法正好相反

class Solution {
    public String longestPalindrome(String s) {
        int length = s.length();
        if (length <= 1) {
            return s;
        }
        int maxLength = 1;
        int begin = 0;
        for (int i = 0; i < length; i++) {//遍历字符串s,把每一位都当作扩展的中心
            int pLengthOdd = expand(s, i, i);//以i为索引的数为中心,扩展寻找奇数回文串
            int pLengthEven = expand(s, i, i + 1);//以i为索引的数为中心,扩展寻找偶数回文串
            int pLength = Math.max(pLengthOdd, pLengthEven);
            if (pLength > maxLength) {
                maxLength = pLength;
                begin = i - (maxLength - 1) / 2;
            }
        } 
        return s.substring(begin, begin + maxLength);
    }

    //扩展搜索,返回最长的回文子串
    public int expand(String s, int left, int right) {
        while (left >= 0 && right <= s.length() - 1) {
            if (s.charAt(left) == s.charAt(right)) {
                left--;
                right++;
            } else {
                break;
            }
        }
        return right - left - 1;//注意这里的返回值
    }