断断续续地看了两天的马拉车算法,可算是给搞明白了(贼开心),这算是自己搞懂的第一个算法了(23333333333333)这个算法照目前自己的理解来看,貌似就只能求个字符串中的回文串(接触这个算法是要求最长的回文串),虽然应用的范围有点少,但还是要学习滴,不然遇到类似的题目就gg了。
可以在线性时间内求得答案,时间复杂度为O(n)。
1、回文串的个数是可奇可偶的,碰上奇数的回文串还可以,如果是偶数的回文串没有着脚点,那就很恼人了。所以马拉车算法会对字符串进行预先处理,然后再求最长的回文串。首先用字符串中没有出现过的字符来表示串中每个元素的间隔,而为了防止在访问时出现越界情况,需要在串首和串尾再加上额外的特殊字符。
例如:原串为ababab;处理完之后就是$#a#b#a#b#a#b#$; 其实对于最后一个$,也可以不加,因为字符串的最后一个字符是‘\0’就相当于一个特殊字符了。
//设t为将要进行预处理的字符串,则处理实现如下 string t="@#"; for(int i=0;i<str.size();i++) { t=t+str[i]; t=t+"#"; }
2、接下来就是在新串中找以每一个字符为中心的回文串就可以了。manacher算法的思想就是从左到右求出以每个字符为中心的最长回文串。设能延伸到最右边的字符串的中心位置为id,该字符串最右端的位置为mx,pal数组来储存此处回文串的长度。因为回文串有对称的性质,所以后边的字符串可以通过对称来直接求得其长度(当然前边的还是需要乖乖的遍历求出来的)。
3、对于遍历到的字符(下标设为i),一共会有三种情况;
(1)i<=mx;
情况如下图所示(第一次用画图的我表示已经被逼疯了emmmmm)
该情况下就万事大吉了,直接把2*id-i处串的长度,复制给i就OK了。
(2)i<=mx
同样是这种情况但是出现i处的回文串超出了mx的情况如下图
最右端超出了mx的范围,出现什么情况就不好说了,所以只能暴力一下,然后更新mx的大小就可以了
(3)i>mx
这种情况直接暴力,求此处回文串的长度即可。附上自己写的代码
int mx=0,id=0,len=0; vector<int> pal(t.size(),0); for(int i=1;i<t.size();i++) { //pal数组存的其实是此处回文串的长度+1 pal[i]=mx>i ? min(pal[2*id-i],mx-i):1;//此处为最关键的一处 //当 mx>i 当然是要选一个小的来赋值,这样就可以防止超出mx了 //然后再接着暴力枚举后边的是不是回文串,以使它的长度增加。 //当 mx<=i时,此处的长度为1,直接进入暴力枚举它的长度 while(t[i+pal[i]]==t[i-pal[i]] && ss.count(t[i+pal[i]])) { ++pal[i]; } //最右端回文串的长度超出mx后就要进行mx和id的更新了 if(mx < i+pal[i]) { mx = i+pal[i]; id=i; } if(len < pal[i]-1) { len=pal[i]-1; } }
OK以上就是自己总结的马拉车算法了。