看到了一个十分不错的manacher教程,原作者用java写的,我这里改成了C++
融汇贯通的自己的理解
原文:老司机开车,教会女朋友什么是「马拉车算法」_吴师兄学编程 (cxyxiaowu.com)
对于算法核心的说明,原帖讲的是浅显易懂,这里就拉两张图片,方便日后巩固
#include <bits/stdc++.h>
#include <cstring>
using namespace std;
/*
* 对于变量的说明
* Maxright 是当前拓展的最右边界,其实就是最靠右的回文串最后一个字符的下标
* center 最靠右字符串中心的下表,形式上的定义 center = max{x + p[x] | 0 <= x < i}
* 其中对于每个center,都有唯一的Maxright与其对应,因此,必须同时更新
* mirror是一个i关于center的对称点
*/
char T[20002],T1[10001];
//预处理
void replay()
{
int index = 1;
T[0] = '#';
int len = strlen(T1);
//cout <<"len = " <<len <<endl;
for(int i=0;i<len;i++){
T[index++] = T1[i];
T[index++] = '#';
}
}
//存放地址信息的结构体
struct Address
{
int begin;
int end;
};
//算法实现
/*
* 时间复杂度计算:
* 说明:原串的长度为n
* 对于预处理,对每一个字符添加到新的字符串中时间复杂度为O(n)
* 对于manacher算法,进行2n+1次迭代,对于while循环,总的来说每个字符最多匹配一次,所以进行2n-1次迭代
* while 循环次数是O(n)这个级别
* 对于manacher函数而言O(n)+O(n) = O(n)
* 对于算法本身来说:O(n)+O(n) = O(n)
* 所以总体的时间复杂度就是O(n)级别的
*/
/*
* 对于manacher algorithm 的总结
* 此算法的核心和KPM有些许相似,根据已经匹配的回文串匹配未匹配的回文串
* 其中 p[i] =min(Maxright,p[mirror])的可行性是本算法的重中之重
* 简单的来说,他是所有情况的通解
* 我们可以分三种情况,这三种情况可以归纳为两种情况
* 1.i > maxRight
* 2.i = maxRight
* 3.i < maxRight
* 1 2其实是一种情况,这种情况直接while匹配就OK
* 3 又分为三种情况
* (1) p[mirror] 较小
* (2) p[mirror] 所对应的回文字符串刚好不出最长回文串的边界
* (3) p[mirror] 较大
* 弄懂这三种情况,manacher就拿捏了
* 水平有限,拉一个原帖,特此谢鸣
*/
Address& manacher()
{
auto address = new Address{0,0};//
int len = strlen(T);
int p[20002];
int Maxright = 0,center = 0;
int Maxlen = 0;
for(int i=0;i<len;i++){
if(i<Maxright){
int mirror = 2*center - i;
p[i] = min(Maxright - i,p[mirror]);
}
int left = i - p[i] - 1; //两个用来匹配字符匹配的指针
int right = i + p[i] + 1;
while(left>=0&&right<len&&T[left]==T[right]){
p[i]++;
right++;
left--;
}
if(i+p[i]>Maxright){
Maxright = i + p[i];
center = i;
}
if(p[i]>Maxlen){
Maxlen = p[i];
address->begin = (i - p[i])/2;
address->end = address->begin + Maxlen - 1;
}
}
return *address;
}
int main()
{
cin >> T1;
replay();
Address address = manacher();
for(int i=address.begin;i<=address.end;i++){
cout << T1[i];
}
return 0;
}
对这个图片特别说明:三个黄C必定相等,而最左边的C和紫色的e一定不相等,所以说就有了紫色的e和最右边的c一定不相等,也就是不能匹配,从而确定p[i]的值
dui