Skip to content

Latest commit

 

History

History
95 lines (63 loc) · 3.53 KB

5. Longest Palindromic Substring.md

File metadata and controls

95 lines (63 loc) · 3.53 KB

LeetCode 5. Longest Palindromic Substring

思路

首先,确定这个问题可以用DP来做。

  • 求最值:这个问题求最长回文子串,满足

  • 最优子结构(原问题的解可以通过求解子问题得到,并且子问题之间相互独立)。原问题是求解字符串s的最长回文子串,那我只需遍历所有以字符s[i]结尾的最长回文子串,然后找最大的即可(转换问题)。原问题以字符$s[i]$结尾的最长回文子串可以利用子问题以字符$s[i-1]$结尾的最长回文子串以及字符$s[i]$来求解。以字符$s[i-1]$结尾的最长回文子串与字符$s[i]$相互独立。

  • 重叠子问题:要求$(s[0]...s[i])$的最长回文子串,必然要知道$(s[0]...s[i-1])$的最长回文子串,要求$(s[0]...s[i-1])$必然要先找到$(s[0]...s[i-2])$,所以存在重叠子问题

然后,按一般套路base case -> 状态 -> 选择 -> 确定dp函数或者dp数组定义

  1. base case: 以$s[0]$结尾的最长回文子串是$s[0]$, 长度为1;

  2. 状态:以谁结尾

  3. 选择,其实和第四步耦合在一起

  4. 确定状态转移方程:

    也就是已知$dp[n-1]$,如何求$dp[n]$?

    比如$"****abbac"$,已知$dp[n-1] = 4$,即以$s[n-1]$结尾的最长回文子串为$"abba"$,如何判断以$s[n] = c$结尾的最长回文子串。

    在这里,为了下面叙述的方便,不妨记$P(s[i])$为以s[i]字符结尾的最长回文子串, $isPalindrome(a, b)$为判断字符串s[a...b]是否为回文子串的函数。

    那么首先看穷举如何来算,那必然是依次判断$isPalindrome(0, n)$, 如果不行,再$isPalindrome(1, n)$, ..., 一直到判断$isPalindrome(n-1, n)$。

    显然由于$dp[n-1]$(即$P(s[n-1])$)的限制,我们无需从头开始搜索,只需包含$P(s[n-1])$再加上字符$s[n]$,看看这个子串是不是回文子串,最多呢再比以$s[n-1]$结尾的最长回文子串再往前一个,因为可能存在这种情况(cabbac),然后依次往后搜索,只要找到一个,那必然是对s[n]而言最大的,因为我们是从大往小搜。

    那么状态转移方程就可以写出来:

    $dp[n] = max(PS("s[i-1]...s[n]"), PS("s[i]...s[n]"), ... , PS("s[n-1]s[n]"))$

    $其中 P(s[n-1]) = "s[i]...s[n-1]"$

    $PS("s[i]...s[j]")$表示字符串$"s[i]...s[j]"$的最长回文子串

最后框架套起来:

# base case
dp[0][...] = base case

for 状态1 in 所有取值:
  for 状态2 in 所有取值:
    ...
    dp[状态1][状态2][...] = 取最值选择1选择2,...选择n

代码

class Solution {
    public String longestPalindrome(String s) {
        int n = s.length();
        int[] dp = new int[n];
        int start = 0, maxLen = 1;
        dp[0] = 1;
       
       for (int i = 1; i < n; i++) {
           int begin = Math.max(i-1 - dp[i-1], 0);
           for (int j = begin; j <= i; j++) 
               if (isPalindrome(s, j, i)) {
                   dp[i] = i - j + 1; break;
               }
                    
           if (maxLen < dp[i]) {
               start = i - dp[i] + 1;
               maxLen = dp[i];
           }
        }
        return s.substring(start, start+maxLen);
    }

    public static boolean isPalindrome(String s, int start, int end) {
        int left = start, right = end;
        while (left <= right) {
            if (s.charAt(left) != s.charAt(right))
                return false;
            left++;
            right--;
        }
        return true;
    }
}

还可以用双指针来做