每天学一丢意为每天学一点丢一点。
Manacher
ManacherManacherManacher 算法主要解决的问题是最长回文子串,为了解决奇偶性的问题,我们在原字符串中加入了新的字符作为通配符,例如我们将 abababcabababcabababc 扩充为了 #a#b#a#b#a#b#c#\#a\#b\#a\#b\#a\#b\#c\##a#b#a#b#a#b#c#,因为 #\## 号都是相同的,所以它并不影响匹配关系,但是我们在考虑对称轴的时候,却可以把原本位于两个字符中间的对称轴,变为一个实际存在的字符 #\## 上,这样便于之后的讨论。
在将字符串扩充之后,我们引入了两个变量,maxrmaxrmaxr 和 pospospos,分别表示当前匹配回文串的最后端点,以及这个回文串的中点。
我们顺序访问扩充后的串,对于访问到的节点 iii,它一定在 pospospos 的右侧(pospospos 是已匹配回文串的对称轴):
如果 iii 在 maxrmaxrmaxr 左侧,则我们可以找到一个关于pos对称的位置 jjj,他们已匹配的回文部分应该是相同的。但是这部分回文部分,我们只能保证在 maxrmaxrmaxr 以内的是确定回文的。然后需要再向左右扩展
如果 iii 在 maxrmaxrmaxr 右侧,则我们只能知道长度为1的回文子串,即它本身,之后需要对其分别向左右扩展。
那么基本步骤是:
step1: istep1:\, istep1:i 在 maxrmaxrmaxr 的左侧 p[i]=min(p[2∗pos−i],r−i)p[i] = \min\left(p\left[2*pos-i\right], r-i\right)p[i]=min(p[2∗pos−i],r−i),不然p[i]=1p[i] = 1p[i]=1
step2: step2:\,step2: 对p[i]p[i]p[i]进行扩展
step3: step3:\,step3: 更新 pospospos 和 maxrmaxrmaxr
代码
int p[maxn<<1]; //p[i]-1是以i为中点的回文串长度
char s[maxn], now[maxn<<1]; // now is #a#b#c#
int k;
void Manacher(){
int lens = strlen(s);
now[0] = '#';
rep(i, 0, lens){
now[i<<i] = '#';
now[i<<i|1] = s[i];
}
now[lens<<1] = '#';
now[lens<<1|1] = '\0';
int len = (lens<<1|1);
int pos = 0, maxr = 0;
rep(i, 0, len){
if(i < maxr) p[i] = min(p[2*pos-i], maxr-i);
else p[i] = 1;
while(i-p[i]>=0 && i+p[i]<len && now[i-p[i]] == now[i+p[i]])
p[i]++;
if(i+p[i] > maxr){
pos = i;
maxr = i+p[i];
}
}
}