5.最长回文子串

LeetCode参考:https://leetcode.cn/problems/longest-palindromic-substring/description/

算法设计思路

回文串的性质:对于一个子串而言,如果它是回文串,并且长度大于 2 2 2,那么将它首尾的两个字母去除之后,它仍然是个回文串。例如对于字符串 “ababa”,如果我们已经知道 “bab”是回文串,那么 “ababa”一定是回文串。

原问题的解可以由子问题得到,因此可以使用动态规划解决该问题。

我们用 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示字符串 s s s i i i 开始,以 i i i 结尾的回文串长度:

  • 若不是回文串,则 d p [ i ] [ j ] = 0 dp[i][j] = 0 dp[i][j]=0;
  • 若是回文串,则 d p [ i ] [ j ] dp[i][j] dp[i][j] 记录回文串的长度。

在填充 d p dp dp 数组的过程中,从斜对角线依次往右上填充:

  • 如果 i > j i > j i>j,则根本不是合法的子串,填充为 0 0 0
  • 如果 i = = j i == j i==j,即为斜对角线上的元素,填充为 1 1 1
  • 如果子串长度为 2 2 2,判断 s [ i ] = = s [ j ] s[i]==s[j] s[i]==s[j]
    • 如果 s [ i ] = = s [ j ] s[i]==s[j] s[i]==s[j],则 d p [ i ] [ j ] = 2 dp[i][j] = 2 dp[i][j]=2;
    • 否则,保持为 0 0 0 不变。
  • 如果子串长度不小于 3 3 3,判断 s [ i ] = = s [ j ] s[i]==s[j] s[i]==s[j] s [ i + 1 , j − 1 ] s[i+1, j-1] s[i+1,j1] 是否为回文串:
    • 如果 s [ i ] = = s [ j ] s[i]==s[j] s[i]==s[j] s [ i + 1 , j − 1 ] s[i+1, j-1] s[i+1,j1] 为回文串,则 $ dp[i][j] = dp[i + 1][j - 1] + 2$;
    • 否则,保持为 0 0 0 不变。

在填充过程中设置”锚点“:rowcol,用于回到每一个斜列开始位置。

在求出 d p dp dp 数组后,遍历 d p dp dp 数组,找到最长回文子串的长度,根据 i i i j j j 构造出解,即最长回文子串。

s = s = s= b a b a d babad babad” 为例,算法的运行过程如下:

源码

class Solution {
    public String longestPalindrome(String s) {
        if (s == "" || s.length() == 0) {
            return "";
        }

        int n = s.length();

        // dp[i][j] 表示字符串索引从 i 到 j 的子串是不是回文串,如果是,填写回文串长度,否则,填充0
        int[][] dp = new int[n][n];


        // 最长回文子串
        String str = null;

        int i = 0; // 行
        int j = 0; // 列

        // 锚点,记录每一斜列的开始位置
        int row = 0; 
        int col = 0;

        // 填充dp数组
        while (row < n && col < n) {
            // i == j, 只有一个字符的字符串
            while (i == j && i < n && j < n) {
                dp[i][j] = 1;
                i++;
                j++;
            }

            // 子串长度为2
            while ((j - i + 1) == 2 && i < n && j < n) {
                if (s.charAt(i) == s.charAt(j)) {
                    dp[i][j] = 2;    
                }
                i++;
                j++;
            }

            // 子串长度不小于3
             while ((j - i + 1) >= 3 && i < n && j < n) {
                // 如果s[i..j]是回文子串,那么去掉s[i]和s[j]也是回文子串
                if (s.charAt(i) == s.charAt(j) && (dp[i + 1][j - 1] != 0)) {
                    dp[i][j] = dp[i + 1][j - 1] + 2;
                }
                i++;
                j++;
            }
            i = row;
            j = col + 1;
            row = i;
            col = j;
        }
        

        // 遍历dp数组,找出最长回文子串的长度,构造解
        int maxLen = 0;
        for (int p = 0; p < n; p++) {
            for (int q = 0; q < n; q++) {
                if (dp[p][q] == 0 ) continue;
                if (dp[p][q] > maxLen) {
                    maxLen = dp[p][q];
                    str = s.substring(p, q + 1);
                }
            }
        }
        return str;
    }
}
posted @ 2023-07-14 22:45  gengduc  阅读(8)  评论(0编辑  收藏  举报  来源