Longest Palindromic Substring-----LeetCode進階路⑤
阿新 • • 發佈:2018-12-10
- 題目描述
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
給定一個字串s,找出s中最長的迴文子字串。可假設s的最大長度是1000。
Example 1:
Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.
Example 2:
Input: "cbbd"
Output: "bb"
- 思路分析
對於迴文串,大家一定都不陌生啦,即正讀,反讀都一樣的字串。
求解最長迴文子串問題,必須是馬大神的Manacher's Algorithm,時間複雜度O(n),線性搞定,完虐中心擴充套件和動態規劃這些思路
Manacher's Algorithm思路如下:
- 對給定字串進行預處理:
①字元兩兩之間和字串頭尾都插入任一特殊字元,確保處理後的字串長度一定為奇數,不必再分情況列出奇偶兩種情況。eg:abba (4)—>$a$b$b$a$(9) cde(3)—>$c$d$e$(7)
②在處理後的字串前後各加一個相異的特殊字元,防止字元越界問題 - 開一個數組c來儲存以B中的第n個元素為中心的最大回文子串,即B[n]為中心的最大回文子串(為了方便起見,原始字串稱A,處理後的字串稱B,新陣列稱為c)
eg:B $ a $ b $ b $ a $
c 0 1 0 1 4 1 0 1 0
從eg中,我們可以發現c[n] 即以A[n]為中心的最長迴文子串長度
所以找到迴文子串的最大長度等價於:找到最大的c[n] - 增加兩個幫手,pos來表示最大回文子串的中心,max(=pos+c[pos])表示最大回文子串的半徑。
- 下面進入大餐,充分利用迴文串 內迴文 的對稱性,這裡我怕說不好,貼一下大神的助攻下
https://articles.leetcode.com/longest-palindromic-substring-part-ii/
- 原始碼附錄
class Solution {
public String longestPalindrome(String s) {
if(s.length()<=1){
return s;
}
StringBuilder sb = new StringBuilder();
sb.append("#");
for(int i=0;i<s.length();i++){
sb.append("$");
sb.append(s.charAt(i));
}
sb.append("$*");
int pos = 0;
int r = 0;
int mi = 0;
int ms = 0;
int[] t = new int[sb.length()];
for(int i=1;i<sb.length();i++){
int mirrorCur = 2*pos-i;
t[i] = (i>r) ? 1:(Math.min(t[mirrorCur],r-i));
while((i+t[i])<sb.length() && (sb.charAt(i+t[i]) == sb.charAt(i-t[i]))){
t[i] ++;
}
if(i+t[i] > r){
r = t[i] + i;
pos = i;
}
if(t[i]>ms){
ms = t[i];
mi = i;
}
}
return s.substring((mi-ms)/2,(mi+ms)/2-1);
}
}
自行理解,實在不理解的話,參照下文詳解版
class Solution {
public String longestPalindrome(String s) {
if(s.length()<=1){
return s;
}
StringBuilder sb = new StringBuilder();//進行預處理
sb.append("#");
for(int i=0;i<s.length();i++){
sb.append("$");
sb.append(s.charAt(i));
}
sb.append("$*");
int pos = 0;//當前情況下能夠到達的最遠的迴文子串中心
int r = 0;//當前情況下能到達的最遠迴文子串半徑
int mi = 0;//記錄最長的迴文子串中心
int ms = 0;//記錄最長的迴文子串半徑
int[] t = new int[sb.length()];
for(int i=1;i<sb.length();i++){
int mirrorCur = 2*pos-i;//記錄當前下標和pos的對稱點
/*
if(i<r){//當前下標小於當前能夠到達的最遠迴文子串的半徑時,用下標的t值
t[i] = Math.min(t[mirrorCur],r-i)
}
else{//負責置1等待
t[i] = 1;
}
*/
t[i] = (i>r) ? 1:(Math.min(t[mirrorCur],r-i));
//檢查並更新以當前下標為中心的迴文子串到達的最遠長度
while((i+t[i])<sb.length() && (sb.charAt(i+t[i]) == sb.charAt(i-t[i]))){
t[i] ++;
}
if(i+t[i] > r){ //檢查並更新當前能夠到達的最遠的迴文子串的資訊
r = t[i] + i;
pos = i;
}
if(t[i]>ms){ //更新當前已知的最長的迴文串子資訊
ms = t[i];
mi = i;
}
}
return s.substring((mi-ms)/2,(mi+ms)/2-1);//去掉輔助符號 進行輸出
}
}