manacher模板

转自:http://blog.csdn.net/zzkksunboy/article/details/72600679

作用

线性时间解决最长回文子串问题。

思想

Manacher充分利用了回文的性质,从而达到线性时间。 
首先先加一个小优化,就是在每两个字符(包括头尾)之间加没出现的字符(如%),这样所有字符串长度就都是奇数了,方便了很多。 
abcde->#a%b%c%d%e$ 
记录p[i]表示i能向两边推(包括i)的最大距离,如果能求出p,则答案就是max(p)-1了(以i为中点的最长回文为2*p[i]-1,但这是加过字符后的答案,把加进去的字符干掉,最长回文就是p[i]-1)。

我们假设p[1~i-1]已经求好了,现在要求p[i]: 
假设当前能达到的最右边为R,对应的中点为pos,j是i的对称点。 
1.当i<R时 
这里写图片描述 
由于L~R是回文,所以p[i]=p[j](i的最长回文和j的最长回文相同)。 
这里写图片描述 
这种情况是另一种:j的最长回文跳出L了。那么i的最长回文就不一定是j的最长回文了,但蓝色的肯定还是满足的。

综上所述,p[i]=min(p[2*pos-i],R-i)。 
2.当i>=R时 
由于后面是未知的,于是只能暴力处理了。

效率

但是这样看起来很暴力,为什么复杂度是O(len)的呢?因为R不会减小,每次暴力处理的时候,p[i]增大多少,就说明R增大多少,而R最多增加len次。所以复杂度是O(len)的。

推论

(Manchery大神告诉我的,Orz) 
一个串本质不同的回文子串个数最多为len个,证明方法和效率差不多:每次p[i]增加的时候,就说明出现了新的本质不同的回文子串。

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 const int N=1e6+5;
 7 
 8 int len1,len2;                              //len1为s长度,len2为str长度
 9 int p[N];                                   //p[i]为点i处的最长回文半径
10 char s[N],str[N];                           //s为原字符串,str为扩充后的字符串
11 
12 void init(){
13     str[0]='$';
14     str[1]='#';
15     for(int i=0;i<len1;i++){
16         str[i*2+2]=s[i];
17         str[i*2+3]='#';
18     }
19     len2=len1*2+2;
20     str[len2]='%';
21 }
22 
23 void manacher(){
24     int id=0,mx=0;                          //mx记录回文串延伸的最远位置,id则为对应mx的点,mx=p[id]+id;
25     for(int i=1;i<len2;i++){
26         if(mx>i) p[i]=min(p[2*id-i],mx-i);  //点i在mx之内
27         else p[i]=1;                        //在mx之外则p[i]直接初始化为1
28         while(str[i+p[i]]==str[i-p[i]])     //暴力匹配
29             p[i]++;
30         if(p[i]+i>mx){                      //更新mx,id
31             mx=p[i]+i;
32             id=i;
33         }
34     }
35 }
原文地址:https://www.cnblogs.com/fu3638/p/8504144.html