標籤:
題目:
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.
思路:
這題其實就是問 Manacher 演算法——O(n)迴文子串演算法。迴文串開始比較糾結的是奇偶串判別方法不同,用一個比較巧妙的方法將其統一成奇串:先在每兩個相鄰字元中間插入一個分隔字元,這個分隔字元要在原串中沒有出現過,一般可以用‘#’分隔。這樣迴文串保證了都是奇數。用一個數組 p 記錄以每個字元為中心的最長迴文串長度,即:p[id] 記錄的是以字元 s[id] 為中心的最長迴文串長度,舉例:
原串 : a b c d c b a f
拷貝串 : # a # b # c # d # c # b # a # f #
數組 p : 1 2 1 2 1 2 1 8 1 2 1 2 1 2 1 2 1
容易證明,p[id] - 1 就是迴文子串在原串中的長度,如上例中,最長子串為 abcdcba ,長度為7,p[8] - 1 = 7。現在問題變成如何算出數組 p 。用變數 i 逐個掃描字串,變數 MaxId 記錄掃描串時所到達的最右邊的下標,用 id 記錄掃描到最右邊串時當前子串的中心,結合以下代碼理解下變數意思:
if (MaxId < i + p[i]){ MaxId = i + p[i]; id = i;}
要在O(n)時間內找到最長迴文串,也就是說當掃描子串時最長掃到了 MaxId 的位置,那麼在 i 遍曆到 MaxId 之前我們都不需要重新掃描,而是能直接通過某個性質得到當前 p[i] ,先給出核心代碼:
if (MaxId > i) p[i] = min(MaxId - i, p[id + id - i]);else p[i] = 1;
id + id - i 是 i 關於 id 的對稱點,設為 j ,p[j] 是以 j 為中心的最長迴文串長度,也就是以 j 為中心掃描過的長度,MaxId 是當前掃描到的最右邊,結合理解:我們在算 p[i] 時,由於迴文串的特點可以知道 p[i] 就等於 p[j] ,但是若 i + p[i] 的長度超過了 MaxId ,這之後的字元是還未掃描過的,即在計算 p[i] 時,它所能確定的最長不能超過 MaxId - i ,也不能超過 i 的對稱點 j 所掃描的長度 p[j] ,所以取兩者間的小值。見圖:
p[i] = p[j];
p[i] = MaxId - i;
代碼:
1 #define min(a,b) (a) > (b)? (b): (a) 2 char* longestPalindrome(char* s) { 3 char auxStr[2001]; 4 int i, p[2001] = {0}, MaxId = 0, id = 0, leng, j, start = 0; 5 6 auxStr[0] = ‘^‘; 7 for (i = 0; s[i] != ‘\0‘; ++i){ 8 auxStr[i+1+i+1] = s[i]; 9 auxStr[i+1+i] = ‘#‘;10 }11 auxStr[i+1+i] = ‘#‘;12 auxStr[i+1+i+1] = ‘\0‘;13 for (i = 1; auxStr[i] != ‘\0‘; ++i){14 if (MaxId > i)15 p[i] = min(MaxId - i, p[id + id - i]);16 else17 p[i] = 1;18 while (auxStr[i + p[i]] == auxStr[i - p[i]])19 ++p[i];20 if (MaxId < i + p[i]){21 MaxId = i + p[i];22 id = i;23 }24 if (p[start] < p[i])25 start = i;26 }27 i = (start - p[start])/2;28 leng = p[start] - 1;29 for (j = 0; j < leng; ++i){30 auxStr[j++] = s[i];31 }32 auxStr[j] = ‘\0‘;33 return auxStr;34 }Longest Palindromic Substring
Longest Palindromic Substring