迴文和迴文子串
迴文串:順着讀和倒着讀都一樣的字符串;
迴文子串:給定字符串string,若str同時滿足以下兩個條件:1)str是string的子串;2)str是迴文串;那麼str就是string的迴文子串;
引出問題
要求求出上面string中最長的那個迴文子串;
解決方案
方案一:枚舉中心位置,對奇數位串和偶數位串分開處理;
int AllAlgorithms::longestPalindrome(const char *s, int n){
int max,j;
if (0 == s || n < 1) {
return 0;
}
max = 0;
for (int i = 0; i < n ; ++ i) {//i爲迴文串的中心
for ( j = 0; ((i - j) >= 0) && ((i+j) < n); ++j) {//如果迴文串的長度是奇數
if (s[i-j] != s[i+j]) {
break;
}
if ((j * 2 + 1) > max) {
max = 2*j + 1;
}
}
for (j = 0; ((i-j) >= 0) && ((i+j+1) < n); ++j) {//如果迴文串的長度是偶數
if (s[i-j] != s[i+j+1]) {
break;
}
if ((j * 2 + 2) > max) {
max = 2*j + 2;
}
}
}
return max;
}
該方案比較耗時累贅,因爲在判斷一個串是否是迴文串時,需要考慮奇數和偶數位的而不同,要分開處理,就造成了代碼的大量冗餘,因此該方法不可取;
方案二:Manacher算法
對於一個長度爲n的字符串,考慮如下問題:
長度爲n的字符串,一定有n-1個“鄰接間隙”,在加上首字符的前面,以及尾部字符的後面,一共有n-1+2=n+1個“gap”,然後再加上字符串本身,一共有字符n+n+1=2n+1個,這樣一擴展,可以發現,它一定爲奇數,因此這樣做了擴展後,我們可以只考慮奇數的情況,無需考慮偶數的情況,大大簡化了代碼;
下面我們就來介紹有關Manacher算法的解決思路是什麼樣的:
介紹之前先給出兩個trick:
1)爲處理一致,在字符串最前面加上一位未出現過的字符,如$:
2)用一個數組P[i]來記錄以字符S[i]爲中心的最長迴文子串向左或向右擴張的長度(包括S[i]),如:
算法的任務:在P[0...i-1]已知的前提下,計算P[i]的值,換句話說就是,在P[0...i-1]已知的前提下,能否爲P[i]的計算提供有用的信息;
算法核心:
1)簡單遍歷,得到i個三元組{k,P[k],k+P[k]},0≤k≤i-1;以k爲中心的字符形成的最大回文子串的最右位置是k+P[k]-1;
2)以k+P[k]爲關鍵字,找出上述i各三元組中,k+P[k]最大的那個三元組,記作(id,P[id],id+P[id]),爲了簡化起見,進一步記mx=id+P[id],於是得到三元組爲(id,P[id],mx),含義就是:在上述遍歷得到的所有三元組中,向右到達最遠的位置,就是mx;
3)在計算P[i]的時候,考查i是否落在了區間[0,mx)中:若i落在區間[0,mx)的右側,說明區間[0,mx)沒能控制住i,P[0...i-1]已知的前提下不能爲P[i]的計算提供有用的信息;若i落在mx的左側,則說明[0,mx)控制住了i,可以提供一些有用的信息;
下面我們用圖示來看一下Manacher算法的遞推關係:
1)
記i關於id的對稱點爲j,j=2*id-i,如果此時滿足:mx-i >P[j],則:
記my爲mx關於id的對稱點:my = 2*id-mx;
因爲以S[id]爲中心的最大回文子串爲:S[my+1...id...mx-1],而S[my+1...id]與S[id...mx-1]對稱,i和j也關於id對稱,所以P[i]=P[j];
2)
上圖中,同樣的:
記i關於id的對稱點爲j,j=2*id-i,如果此時滿足:mx-i < P[j],則:
記my爲mx關於id的對稱點:my = 2*id-mx;
因爲以S[id]爲中心的最大回文子串爲:S[my+1...id...mx-1],而S[my+1...id]與S[id...mx-1]對稱,i和j也關於id對稱,所以P[i]至少是等於上圖中綠色框部分,即等於mx-i;
void AllAlgorithms::Manacher(char *s, int *p){
int size = (int)strlen(s);
p[0] = 1;
int id = 0,mx = 1;
for (int i = 1; i < size; i ++) {
if (mx > i) {
p[i] = min(p[2*id - i], mx- i);
}
else
p[i] = 1;
for (; s[i+p[i]] == s[i- p[i]]; p[i] ++);
if (mx < i + p[i]) {
mx = i + p[i];
id = i;
}
}
}
int AllAlgorithms::min(int a, int b){
return ((a+b) - abs(a-b))/2;
}