百度2014研發類校園招聘筆試題解答
-
當i==j時,P[i][j]=true
-
當i+1==j時,P[i][j]=str[i]==str[j]
-
其他,P[i][j]=P[i+1][j-1]&&(str[i]==str[j])
那麼P[i][j]中j-i+1最大的且值為true的就是最長迴文子串。這樣,這個方法的時間複雜度為O(n^2),空間複雜度為O(n^2)。比暴力法有很大的改進。
Source Code:
#include <stdio.h> #include <stdlib.h> #include <string.h> intlongestPalSubstr(char *str) { int n = strlen(str); int i, j, len, maxlen = 0, maxi = 0, maxj = 0; bool **P = (bool**)malloc(sizeof(bool) * n); for(i = 0; i < n; i++) { P[i] = (bool*)malloc(sizeof(bool) * n); } // initialize P[i][i] for(i = 0; i < n; i++) { P[i][i]= true; } // compute P[n][n] by length for(len = 2; len <= n; len++) { for(i = 0; i < n - len + 1; i++) { j = i + len - 1; if(len == 2) { P[i][j] = (str[i] == str[j]); } else { P[i][j]= ((str[i] == str[j]) && P[i + 1][j - 1]); } } } // int k; for(i = 0; i < n; i++) { for(j = i; j < n; j++) { // printf("%d ", P[i][j]); if(P[i][j] && maxlen < (j - i + 1)) { maxlen = j - i + 1; maxi = i; maxj = j; } } // printf("\n"); // for(k = 0; k <= i; k++) // printf(" "); } printf("The longest palin substr is "); for(i = maxi; i <= maxj; i++) { printf("%c", str[i]); } printf(", maxlen is %d\n\n", maxlen); return maxlen; } int main() { char str[100]; while(1) { gets(str); if(strlen(str) == 0) break; longestPalSubstr(str); } return 0; }
演算法3:第三個方法,可以從上面那個方法的狀態轉移方程獲得啟發,對於每一個迴文子串可以先確定一箇中心,然後向兩邊擴充套件,這樣可以在時間複雜度O(n^2),空間複雜度O(1)的情況下完成,需要注意的是,長度為奇數和偶數的中心的情況是不同的。
Source Code:
#include <stdio.h> #include <stdlib.h> #include <string.h> int longestPalSubstr(char *str) { int len = strlen(str); int i, maxLen = 1, start = 0; int low, high; // 將每個字元作為中心向兩邊擴充套件判斷 for(i = 1; i < len; i++) { // 處理長度為偶數的情況 low = i - 1; high = i; while(low >= 0 && high < len && str[low] == str[high]) { if(maxLen < high - low + 1) { start = low; maxLen = high - low + 1; } low--; high++; } // 處理長度為奇數的情況 low = i - 1; high = i + 1; while(low >= 0 && high < len && str[low] == str[high]) { if(maxLen < high - low + 1) { start = low; maxLen = high - low + 1; } low--; high++; } } printf("The longest palin substr is "); for(i = start; i < start + maxLen; i++) { printf("%c", str[i]); } printf(", maxlen is %d\n\n", maxLen); return maxLen; } int main() { char str[100]; while(1) { gets(str); if(strlen(str) == 0) break; longestPalSubstr(str); } return 0; }
演算法4:第四個方法採用字尾陣列,將最長迴文子串的問題轉化為最長公共字首的問題。具體的做法就是:將整個字串翻轉之後,拼接到原字串後,注意用特殊字 符分開,這樣問題就變成了新的字串的某兩個字尾的最長公共字首的問題了。這個方法比較強大,很多字串的問題都能夠巧妙的解決。不過實現起來也相對比較:難,好的實現和差的實現時間複雜度相差很大。由於對字尾陣列不是很清楚,未寫程式碼,等學習了字尾陣列再過來補。
演算法5:第五個方法叫做Manacher演算法,是一種線性時間的方法,非常巧妙。首先,我們在上面的方法中個,都要考慮迴文長度為奇數或者偶數的情況。這個:方法,引入一個技巧,使得奇數和偶數的情況統一處理了。具體做法如下:
abba轉換為#a#b#b#a#,也就是在每一個字元兩邊都加上一個特殊字元。
然後建立一個新的P[i]表示,以第i個字元為中心的迴文字串的半徑。例如上面的例子,對應的P如下,設S為原始字串:
S | # | a | # | b | # | b | # | a | # |
P | 1 | 2 | 1 | 2 | 5 | 2 | 1 | 2 | 1 |
通過觀察上面的表,大家可以發現P[i]-1就是實際迴文字串的長度。如果知道P,遍歷一次就知道最長的迴文子串。可以該如何計算P呢?這是這個演算法最核心的部分。
下面的討論基本轉自部落格:http://www.felix021.com/blog/read.php?2040 該部落格中對Manacher演算法介紹得也非常好,向大家推薦。
演算法引入兩個變數id和mx,id表示最長迴文子串的中心位置,mx表示最長迴文字串的邊界位置,即:mx=id+P[id]。
在這裡有一個非常有用而且神奇的結論:如果mx > i,那麼P[i] >= MIN(P[2 * id - i], mx - i) 分開理解就是:
-
如果mx - i > P[j], 則P[i]=P[j]
-
否則,P[i] = mx - i.
這兩個該如何理解呢?具體的解釋請看下面的兩個圖。
(1)當 mx - i > P[j] 的時候,以S[j]為中心的迴文子串包含在以S[id]為中心的迴文子串中,由於 i 和 j 對稱,以S[i]為中心的迴文子串必然包含在以S[id]為中心的迴文子串中,所以必有 P[i] = P[j],見下圖。
(2)當 P[j] >= mx - i 的時候,以S[j]為中心的迴文子串不一定完全包含於以S[id]為中心的迴文子串中,但是基於對稱性可知,下圖中兩個綠框所包圍的部分是相同的,也就是 說以S[i]為中心的迴文子串,其向右至少會擴張到mx的位置,也就是說 P[i] >= mx - i。至於mx之後的部分是否對稱,就只能老老實實去匹配了。
對於 mx <= i 的情況,無法對 P[i]做更多的假設,只能P[i] = 1,然後再去匹配了。
理解了上面的一點,就沒有問題了。
Source Code:
#include <stdio.h> #include <stdlib.h> #include <string.h> int longestPalSubstr(char *str) { char s[100]; int i, maxLen = 1, start = 0, j; int len = strlen(str); int mx = 0, id = 0, min; s[0] = '$'; s[1] = '#'; for(i = 0, j = 2; i < len; i++, j += 2) { s[j] = str[i]; s[j + 1] = '#'; } s[j] = '\0'; len = len * 2 + 1; int *p = (int *)malloc(sizeof(int) * len); memset(p, 0, len); p[0] = 1; for(i = 1; i < len; i++) { min = p[2 * id - i] > (mx - i) ? (mx - i) : p[2 * id - i]; p[i] = mx > i ? min : 1; while(s[i + p[i]] == s[i - p[i]]) { p[i]++; } if(i + p[i] > mx) { mx = i + p[i]; id = i; } } for(i = 0; i < len; i++) { //printf("%d ", p[i]); if(maxLen < p[i] - 1) { maxLen = p[i] - 1; start = i - maxLen; } } printf("The longest palin substr is "); for(i = start; i < start + 2 * maxLen + 1; i++) { if(s[i] != '#') { printf("%c", s[i]); } } printf(", maxlen is %d\n\n", maxLen); return maxLen; } int main() { char str[100]; while(1) { gets(str); if(strlen(str) == 0) break; longestPalSubstr(str); } return 0; }