Manacher算法,别称马拉车
题目:怎么找出一个字符串中回文的最长大小
ABAKK 最长回文为ABA,长度为3;abaaab最长回文为baaab,长度为5
代码
int len = str.length();
char* char_list = new char[len*2 + 1];
int * num_list = new int[len*2+1];
char_list[0] = '$';
int index = 0;
for(int i=1; i < len * 2 + 1; i++)
{
char_list[i] = (i % 2 == 1)? str[index++] : '#';
}
// right_edge, curr_
int right_edge = -1;
int cur_center = -1;
int max_len = 0;
for(int i = 0; i < len * 2 + 1; i++)
{
num_list[i] = (right_edge > i) ? min(num_list[2*cur_center - i], right_edge - i): 1;
while(char_list[i + num_list[i]] == char_list[i - num_list[i]])
num_list[i]++;
if(right_edge < i + num_list[i])
{
right_edge = num_list[i] + i;
cur_center = i;
}
if(max_len < num_list[i])
{
max_len = num_list[i];
}
}
delete[] char_list;
delete[] num_list;
cout << max_len -1 << "\n";
算法解读
马拉车算法的核心思想是利用已有的匹配对算法进行加速,关键点在于cur_center、right_edge两个变量的使用,其中
right_edge: 表示当前匹配的已cur_center为中心点的最右边界(算法从左向右匹配),即以cur_center为中心点的区间【2cur_center - right_edge, right_edge】为一个回文字符串。
记录这两个点的好处在于,当遍历下标值i < right_edge时,我们可以知道,此时的i点存在回文,(原因是i在上述区间【2cur_center - right_edge, right_edge】中,i的回文长度半径要么与它基于cur_center的对称点一致,要么为i到right_edge的距离,取决于哪个比较小),不理解的看图
如图所示,i相对于cur_center的对称点,画一个回文圈圈,由于对称原因,i点也有这么一个圈圈,如果这个圈圈没超出对称范围,则两边圈圈半径是一致的;如果这个圈圈超出了对称范围,那么i的圈圈只能半径只能在对称范围以内。
陈述上面这些主要是要理清马拉车的加速核心,就是直接利用已有的匹配(i的对称点的圈圈范围,跳过部分比较),剩下的只能用暴力比较法了。
算法其他
这个算法一开始用$、#这些符号插入到原字符串中,其一个作用主要是讲所有存在的奇、偶回文都变成奇回文,如AA => A#A 或 #A#A#,ABA=> A#B#A 或 #A#B#A#,方便处理。其他的作用我暂时没想到,有知道的朋友可以留言。