[譯]最長回文子串(Longest Palindromic Substring) Part I


[譯]最長回文子串(Longest Palindromic Substring) Part I

英文原文鏈接在(http://leetcode.com/2011/11/longest-palindromic-substring-part-i.html

 

+BIT祝威+悄悄在此留下版了個權的信息說:

問題:給定字符串S,求S中的最長回文子串。

這個有趣的問題常常在面試中出現。為什么呢?因為解決辦法有很多種。單單我知道的就有5種。你能解決這個問題嗎?來Online Judge試試看吧!

提示

首先你要知道回文是什么。回文就是從左右兩邊讀都一樣的字符串。例如”aba”是回文,”abc”不是回文。

一個常見的錯誤

有人很快會想到這樣一個方法。這個方法有缺陷,不過很容易修正。

翻轉S成為S’。查找S和S’最長公共子串,就是S的最長回文子串。

 

看起來有道理的樣子。用實例檢驗下。

例如S=”caba”,S’=”abac”。

S和S’的最長公共子串是”aba”,確實是S的最長回文子串。

 

再看個例子。

S=”abacdfgdcaba”,S’=”abacdgfdcaba”。

S和S’的最長公共子串是”abacd”,不過很明顯這不是回文。

 

暴力窮舉法O(N3)

最簡單的就是暴力窮舉(Brute Force)對每個start和end位置的子串進行檢測,判斷其是否回文。顯然有C(N,2)(組合)個子串。檢測每個子串都需要O(N)的時間,所以此方法的時間復雜度為O(N3)。

動態規划法O(N2)時間O(N2)空間

我們可以用動態規划(Dynamic Programming即DP)法對暴力窮舉法進行改進。記住,訣竅就是避免重復計算(即重復檢測同一子串)。考慮這個例子”ababa”。如果我們已經檢測過”bab”是回文,那么只需判斷一下最左右的兩個字符(即兩個a)是否相同即可判定”ababa”是否回文了。

總結起來就是:

定義二維數組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)

這個DP法的思路就是,首先可以知道單個字符和兩個相鄰字符是否回文,然后檢測連續三個字符是否回文,然后四個。。。

此算法時間復雜度O(N2),空間復雜度O(N2)。偽代碼如下。

 1 string longestPalindromeDP(string s) {
 2   int n = s.length();
 3   int longestBegin = 0;
 4   int maxLen = 1;
 5   bool table[1000][1000] = {false};
 6   for (int i = 0; i < n; i++) {
 7     table[i][i] = true;
 8   }
 9   for (int i = 0; i < n-1; i++) {
10     if (s[i] == s[i+1]) {
11       table[i][i+1] = true;
12       longestBegin = i;
13       maxLen = 2;
14     }
15   }
16   for (int len = 3; len <= n; len++) {
17     for (int i = 0; i < n-len+1; i++) {
18       int j = i+len-1;
19       if (s[i] == s[j] && table[i+1][j-1]) {
20         table[i][j] = true;
21         longestBegin = i;
22         maxLen = len;
23       }
24     }
25   }
26   return s.substr(longestBegin, maxLen);
27 }

 

提問:空間復雜度還能再改進嗎?

更簡單的算法O(N2)時間O(1)空間

下面介紹一個O(N2)時間O(1)空間的算法。

回文的特點,就是中心對稱。對於有N個字符的字符串S,只有2N-1個中心。

為何是2N-1?因為兩個字符之間的空檔也可以是一個中心。例如”abba”的兩個b中間就是一個中心。

圍繞一個中心檢測回文需要O(N)時間,所以總的時間復雜度是O(N2)。

 1 string expandAroundCenter(string s, int c1, int c2) {
 2   int l = c1, r = c2;
 3   int n = s.length();
 4   while (l >= 0 && r <= n-1 && s[l] == s[r]) {
 5     l--;
 6     r++;
 7   }
 8   return s.substr(l+1, r-l-1);
 9 }
10  
11 string longestPalindromeSimple(string s) {
12   int n = s.length();
13   if (n == 0) return "";
14   string longest = s.substr(0, 1);  // a single char itself is a palindrome
15   for (int i = 0; i < n-1; i++) {
16     string p1 = expandAroundCenter(s, i, i);
17     if (p1.length() > longest.length())
18       longest = p1;
19  
20     string p2 = expandAroundCenter(s, i, i+1);
21     if (p2.length() > longest.length())
22       longest = p2;
23   }
24   return longest;
25 }

 

+BIT祝威+悄悄在此留下版了個權的信息說:

PS:“中心檢測法”是我胡謅的名字。

提問O(N)

是否存在O(N)時間的算法?當然有!不過理解起來有點費勁,我們下回分解。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM