LeetCode-最長迴文子串

LeetCode-最長迴文子串

1 Medium-最長迴文子串

給定一個字符串 s,找到 s 中最長的迴文子串。你能夠假設 s 的最大長度爲1000。 php

1.1 示例 1:

輸入: "babad" 輸出: "bab" css

注意: "aba"也是一個有效答案。 html

1.2 示例 2:

輸入: "cbbd" 輸出: "bb" java

2 本身的解答

2.1 思路

  1. 遍歷字符串,而後以當前索引爲中心向兩邊擴展,判斷是否爲迴文.
  2. 是迴文,記錄左邊界和長度,最後返回子串便可.

2.2 代碼

class Solution {
    public String longestPalindrome(String s) {
        if (s.length() < 1) {
            return "";
        }
        // 用做遍歷字符串s
        int i;
        // 用來記錄折半位置
        int len;
        int maxlen = 1;
        int ms = 0;

        // 奇數的迴文子串
        for (i = 0; i < s.length(); i++) {
            int j = i + 1;
            int k = i - 1;
            while (j < s.length() && k >= 0 && s.charAt(j) == s.charAt(k)) {
                len = j - k + 1;
                if (len > maxlen) {
                   // 有更長的迴文串,記錄起始位置
                    maxlen = len;
                    // 迴文從k開始
                    ms = k;
                }
                j++;
                k--;
            }
        }

        // 偶數的迴文子串
        for (i = 0; i < s.length(); i++) {
            int k = i;
            int j = i + 1;
            while (j < s.length() && k >= 0 && s.charAt(k) == s.charAt(j)) {
                len = j - k + 1;
                if (len > maxlen) {
                    maxlen = len;
                    // 迴文從k開始
                    ms = k;
                }
                k--;
                j++;
            }
        }

        return s.substring(ms, ms + maxlen);
    }
}

3 官方解答

3.1 方法一:最長公共子串

3.1.1 常見錯誤

  • 有些人會忍不住提出一個快速的解決方案,不幸的是,這個解決方案有缺陷(可是能夠很容易地糾正):
  • 反轉 S ,使之變成 S' 。找到 SS' 之間最長的公共子串,這也必然是最長的迴文子串。這彷佛是可行的,讓咱們看看下面的一些例子。
  • 例如, S = 「caba」 , S' = 「abac」
  • S 以及 S′ 之間的最長公共子串爲 「aba」 ,偏偏是答案。
  • 讓咱們嘗試一下這個例子: S=「abacdfgdcaba」 , S′=「abacdgfdcaba」S 以及 S′ 之間的最長公共子串爲 「abacd」 ,顯然,這不是迴文。

3.2 算法

咱們能夠看到,當 S 的其餘部分中存在非迴文子串的反向副本時,最長公共子串法就會失敗。爲了糾正這一點,每當咱們找到最長的公共子串的候選項時,都須要檢查子串的索引是否與反向子串的原始索引相同。若是相同,那麼咱們嘗試更新目前爲止找到的最長迴文子串;若是不是,咱們就跳過這個候選項並繼續尋找下一個候選。 這給咱們提供了一個複雜度爲 \(O(n^2)\) 動態規劃解法,它將佔用 \(O(n^2)\) 的空間(能夠改進爲使用 \(O(n)\) 的空間)。 python

3.3 官方解答方法二:暴力法

很明顯,暴力法將選出全部子字符串可能的開始和結束位置,並檢驗它是否是迴文。 算法

3.3.1 複雜度分析

  • 時間複雜度:\(O(n^3)\) ,假設 n 是輸入字符串的長度,則 \(\binom{n}{2} = \frac{n(n-1)}{2}\) 爲此類子字符串(不包括字符自己是迴文的通常解法)的總數。由於驗證每一個子字符串須要 \(O(n)\) 的時間,因此運行時間複雜度是 \(O(n^3)\) 。
  • 空間複雜度:\(O(1)\) 。

3.4 官方解答方法三:動態規劃

爲了改進暴力法,咱們首先觀察如何避免在驗證迴文時進行沒必要要的重複計算。考慮 「ababa」 這個示例。若是咱們已經知道 「bab」 是迴文,那麼很明顯, 「ababa」 必定是迴文,由於它的左首字母和右尾字母是相同的。 sql

咱們給出 P(i,j) 的定義以下: shell

\[ P(i,j) = \begin{cases} \text{true,} &\quad\text{若是子串} S_i \dots S_j \text{是迴文子串}\\ \text{false,} &\quad\text{其它狀況} \end{cases} \] sass

所以, ruby

\[ P(i, j) = ( P(i+1, j-1) \text{ and } S_i == S_j ) \]

基本示例以下:

\[ P(i, i) = true \]

\[ P(i, i+1) = ( S_i == S_{i+1} ) \]

這產生了一個直觀的動態規劃解法,咱們首先初始化一字母和二字母的迴文,而後找到全部三字母迴文,並依此類推…

3.4.1 複雜度分析

  • 時間複雜度: \(O(n^2)\) , 這裏給出咱們的運行時間複雜度爲 \(O(n^2)\) 。
  • 空間複雜度: \(O(n^2)\) , 該方法使用 \(O(n^2)\) 的空間來存儲表。

3.5 官方解答方法四:中心擴展算法

事實上,只需使用恆定的空間,咱們就能夠在 \(O(n^2)\) 的時間內解決這個問題。

咱們觀察到迴文中心的兩側互爲鏡像。所以,迴文能夠從它的中心展開,而且只有 \(2n - 1\) 個這樣的中心。

你可能會問,爲何會是 \(2n−1\) 個,而不是 \(n\) 箇中心?緣由在於所含字母數爲偶數的迴文的中心能夠處於兩字母之間(例如 「abba」 的中心在兩個 ‘b’ 之間)。

3.5.1 複雜度分析

  • 時間複雜度:\(O(n^2)\) , 因爲圍繞中心來擴展迴文會耗去 \(O(n)\) 的時間,因此總的複雜度爲 \(O(n^2)\) 。
  • 空間複雜度: \(O(1)\) 。

3.6 官方解答方法五:Manacher 算法

還有一個複雜度爲 \(O(n)\) 的 Manacher 算法,你能夠在這裏找到詳盡的解釋。然而,這是一個非同尋常的算法,在45分鐘的編碼時間內提出這個算法將會是一個徹徹底底的挑戰。可是,請繼續閱讀並理解它,我保證這將是很是有趣的。

3.6.1 代碼

class Solution {

  /**
   * Transform S to T
   * For example, S = "abba", T="^#a#b#b#a#$".
   * ^ and $ signs are sentinels appended to each end to avoid bounds check
   */
  public String preProcess(String s) {
      int n = s.length();
      if (n == 0) {
          return "^$";
      }
      StringBuilder ret = new StringBuilder("^");
      for (int i = 0; i < n; i++) {
          ret.append("#").append(s.charAt(i));
      }
      ret.append("#$");
      return ret.toString();
  }

  public String longestPalindrome(String s) {
      String T = preProcess(s);
      int n = T.length();
      int[] P = new int[n];
      // Center point
      int C = 0;
      // Right border
      int R = 0;
      // first character is $
      for (int i = 1; i < n - 1; i++) {
          // equals to i' = C - (i - C)
          int iMirror = 2 * C - i;
          P[i] = (R > i) ? Math.min(R-i, P[iMirror]) : 0;

          // Attempt to expand palindrome centered at i
          while(T[i + 1 + P[i]] == T[i - 1 - P[i]]) {
              P[i]++;
          }


          // if palindrome centered at 1 expand past R,
          // adjust center based on expanded palindrome.
          if (i + P[i] > R) {
              C = i;
              R = i + P[i];
          }
      }

      // Find the maximum element in P.
      int maxLen = 0;
      int centerIndex = 0;
      for (int i = 1; i < n - 1; i++) {
          if (P[i] > maxLen) {
              maxLen = P[i];
              centerIndex = i;
          }
      }
      return s.substring((centerIndex - 1 - maxLen) / 2, maxLen);
  }
}

Date: 2018-11-15 15:10

Author: devinkin

Created: 2018-11-15 四 20:40

Validate

相關文章
相關標籤/搜索