5. Longest Palindromic Substring【字符串 DP】
Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
版本1:Brute Force O(n^3)
双指针
——————————————————————————————————
版本2:DP---改进版本1,避免重复计算 O(n^2) | O(n^2)
该方法可以用于收集整个字符串的回文状态,作为子问题。
类似于LCS,是LCS与KMP的变种,若采用LCS是有问题的,因为公共子串未必是回文串
解:
状态:定义二维数组P[i,j]用以表示Si…Sj是回文(true)或不是回文(false)
状态转移方程 : P[i,j] = (P[i + 1, j - 1] && Si ==Sj)
初始条件是:P[i, i]=true,P[i, i + 1] = (Si == Si+1)
C++版( 使用 C++ 、java可以A , Python超时)
class Solution { public: string longestPalindrome(string s) { int l = s.length(); bool p[1000][1000] = {false}; int max_len = 1 , max_beg = 0; p[l-1][l-1] = true; for(int i=0;i<l;i++){ p[i][i] = true; if(s[i] == s[i+1]){ p[i][i+1] = true; max_len = 2; max_beg = i; } } for(int length=3 ;length<=l ;length++){ for(int i=0;i<=l-length;i++){ int j = i+length-1; if(s[i] == s[j] && p[i+1][j-1]){ p[i][j] = true; max_beg = i; max_len = length; } } } return s.substr(max_beg,max_len); } };
Java版
public class Solution { public String longestPalindrome(String s) { int n = s.length(); if( n == 0 ) return s; String str = s.substring(0,1); boolean[][] dp = new boolean[n][n]; for ( int i=0;i<n-1;i++ ){ dp[i][i] = true; if( s.charAt(i) == s.charAt(i+1) ){ str = s.substring(i,i+2); dp[i][i+1] = true; } } dp[n-1][n-1] = true; //初始化完成 O(n) for( int i=n-2;i>=0;i-- ) for( int j=i+2;j<n;j++ ){ dp[i][j] = dp[i+1][j-1] && (s.charAt(i) == s.charAt(j)); if( dp[i][j] == true && j-i+1 > str.length() ) str = s.substring(i,j+1); } return str; } }
Python版
s='gsdabcdcbaee' l = len(s) p = [[False]*l for i in range(l)] # init max_len , max_beg = 1 , 0 p[l-1][l-1] = True for i in range(l-1): p[i][i] = True if s[i] == s[i+1]: p[i][i+1] = True max_len = 2 max_beg = i for length in range(3,l+1): for i in range(0,l-length+1): j = i+length-1 if s[i] == s[j] and p[i+1][j-1]: p[i][j] = True max_beg = i max_len = length print s[max_beg:max_beg+max_len]
——————————————————————————————————
版本3:中心扩展法 O(n^2) | O(1) 该方法用Python可A,因为最坏情况才n^2,时间更接近n
Python
def Palindromic( s , i , j ): l = len(s) curLen = 0 while i>=0 and j<l and s[i] == s[j]: i -= 1 j += 1 curLen = (j - 1) - (i + 1) + 1 return curLen class Solution(object): def longestPalindrome(self, s): start = 0 max_len = 1 for i in range(len(s)): curOdd = Palindromic(s,i,i) if curOdd > max_len: max_len = curOdd start = i - curOdd/2 if i+1<len(s): curEven = Palindromic(s,i,i+1) if curEven > max_len: max_len = curEven start = i + 1 - curEven/2 return s[start:start+max_len]
——————————————————————————————————
版本4:Manacher线性算法 O(n) 改进版本3,
俗称:马拉车算法
Tip1:插入'#'可以一次性解决奇偶回文问题 abcdcba -------------> #a#b#c#d#c#b#a#
Tip2:记录状态,避免重复计算(回文发生大量重叠 时“abacabaaa”)
Tip3:避免越界前后设置别的符号作为界限。
状态:P[i]记录以i对应元素为中心的最长回文串的半径(包含自己)。
状态转移:p[i] = mx>i? min( p[ 2*id-i ] , mx - i ) : 1
该方程分为三种情况
1、 mx>i 且 mx-i > p[ j ] (j=2*id-i) ; --> p[ i ] = p [ j ] = p[ 2*id - i ]
2、mx>i 且 mx-i<=p[ j ] ; ---> p[ i ] >= mx-i 继续匹配。
3、mx<=i 无法利用p数组,继续匹配。
public class Solution { public String longestPalindrome(String s) { //预处理 StringBuffer sb = new StringBuffer("^#"); for( int i=0;i<s.length();i++ ){ sb.append(s.substring(i,i+1)+"#"); } sb.append("$"); String str = sb.toString(); //预处理完毕 int[] p = new int[str.length()]; int mx = 0 , id = 0; for( int i=1;i<str.length()-1;i++ ){ p[i] = mx>i ? Math.min( p[ 2*id-i ] , mx - i + 1 ) : 1; while( str.charAt( i+p[i] ) == str.charAt( i-p[i] )) ++p[i]; if( p[i] > p[id] ) { id = i; mx = i + p[i] - 1; } } return s.substring( (2 * id - mx - 1) / 2 , (mx-1)/2 ); } }
——————————————————————————————————
版本5:后缀树 O(nlog(n))
暂空
——————————————————————————————————
什么时候使用动态规划法:
• Optimal substructure 最优子结构
• Overlapping subproblems 重叠子问题
构建解的方法:
Characterize structure of optimal solution 状态
Recursively define value of optimal solution 状态转移
Compute in a bottom-up manner 自顶向上
——————————————————