Manacher算法
Manachar算法主要是處理字符串中關於回文串的問題的,它可以在 O(n) 的時間處理出以字符串中每一個字符為中心的回文串半徑,由於將原字符串處理成兩倍長度的新串,在每兩個字符之間加入一個特定的特殊字符,因此原本長度為偶數的回文串就成了以中間特殊字符為中心的奇數長度的回文串了。 ------摘自百度百科
奇偶變換:為處理字符串方便,現將給定的任意字符串進行處理,使所有可能的奇數/偶數長度的回文子串都轉換成了奇數長度。具體就是在每個字符的兩邊都插入一個特殊的符號。比如hhjj變成 #h#h#j#j#, aba變成 #a#b#a#;為防止數組越界,可以在字符串的開始加入另一個特殊字符,比如“?#a#b#a#?” 。
用p[i]表示以第i個字符為中心軸,兩邊字符軸對稱的最大半徑。如下圖所示,例如p[5]=3,p[7]=6。該算法利用已知的回文字串信息推斷下一個回文字串的長度,判斷當前字符是否包含在之前的回文字串中,若不包含在其中,令p[i]=1;否則做進一步的推斷。
定義變量mid為前面長度能夠延伸到最右邊的回文子串的中心軸位置,定義最大右邊界right = i + mid,即最大右邊界表示以mid為中心的回文子串最右邊位置的下一個位置。如下圖
//定義變量j為i關於mid的對稱位置,j=2*mid-1
一、right > i的情況
1) 當 right - i >= P[j] 的時候,以j為中心的回文子串包含在以為中心的回文子串中,因為 i 和 j 是對稱的,所以以i為中心的回文子串必然包含在以mid為中心的回文子串中,所以必有 P[i] = P[j]。如下圖
2)當 right - i < p[j] 的時候,以j為中心的回文子串不完全包含於以mid為中心的回文子串中,但是基於對稱性可知,處於以mid為中心的回文子串的部分字符串是絕對匹配的,即以i為中心的回文子串向右至少會延伸到right的位置,也就是說 P[i] >= right - i,其后的部分是否是回文的,就要一個個判斷了。
二、right > i的情況
right <= i,無法利用已知信息對以i為中心的回文字串做出判斷,令P[i] = 1,然后一個個再去判斷
以下為代碼實現(C++)
1 #include <iostream> 2 #include <cstring> 3 4 using namespace std; 5 6 string longestPalindrome(string s) 7 { 8 9 string str = "?#"; 10 for(int i = 0; i < s.size(); i++) //字符串長度奇偶變換,添加'$'防止數組越界 11 { 12 str += s[i]; 13 str += "#"; 14 } 15 str += "?"; 16 //cout<<str; 17 18 int p[str.size()-2]={0}; //定義p[i]數組 19 20 int right = 0, mid = 0; 21 for(int i = 1; i < str.size()-1; i++) 22 { 23 if(right > i) 24 { 25 p[i] = (p[2*mid - i] < (right - i) ? p[2*mid - i] : (right - i)); //計算以i為中心的回文字串的最小長度 26 } 27 else 28 { 29 p[i] = 1; 30 } 31 32 while(str[i - p[i]] == str[i + p[i]]) p[i]++; //回文子串長度延伸 33 34 if(i + p[i] > right) 35 { 36 right = i + p[i]; 37 mid = i; 38 } 39 40 } 41 42 int max = 0, middle=0; 43 for(int i = 1; i < str.size()-1; i++) //生成最終結果 44 { 45 if(p[i] > max) 46 { 47 middle = i; 48 max = p[i]; 49 } 50 } 51 max--; 52 53 int start = middle - max ; 54 int end = middle + max; 55 string result; 56 for(int i = start; i <= end; i++) 57 { 58 if(str[i] != '#' && str[i] != '?') 59 { 60 result += str[i]; 61 } 62 } 63 //cout<<result; 64 return result; 65 }
測試
1 int main() 2 { 3 string s1="xschhsdr"; 4 string s2="vffabbaabbbabaababbbacisd"; 5 cout<<longestPalindrome(s1)<<endl; 6 cout<<longestPalindrome(s2)<<endl; 7 return 0; 8 }
結果