1. 程式人生 > >Longest Palindromic Substring-----LeetCode進階路⑤

Longest Palindromic Substring-----LeetCode進階路⑤

  • 題目描述

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思路如下:

  1. 對給定字串進行預處理:
    ①字元兩兩之間和字串頭尾都插入任一特殊字元,確保處理後的字串長度一定為奇數,不必再分情況列出奇偶兩種情況。eg:abba (4)—>$a$b$b$a$(9)         cde(3)—>$c$d$e$(7)
    ②在處理後的字串前後各加一個相異的特殊字元,防止字元越界問題
  2. 開一個數組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]
  3. 增加兩個幫手,pos來表示最大回文子串的中心,max(=pos+c[pos])表示最大回文子串的半徑。
     
  4. 下面進入大餐,充分利用迴文串 內迴文 的對稱性,這裡我怕說不好,貼一下大神的助攻下
    https://blog.crimx.com/2017/07/06/manachers-algorithm/

    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);//去掉輔助符號 進行輸出
    }
}