Manacher算法解析
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 }
结果