这是上上周天机房一位神仙讲的,(gu)了这么久才来整理(w),神仙讲的基本思路已经全都忘记了,幸好的是神仙写了(blog),吹爆原博浅谈(Manacher)算法,以及原博神仙(ych)!
再吹一波(ych):
太巨了!
(Manacher)是一种(O(n))求回文字符子串的算法。(然后迷惑的记得当时问神仙(ych)一个sha diao问题:子串是连续的嘛?显然这里的回文子串是连续的;
(Solution:)
对于一串字符串,对于其中的每一个字符我们都维护一个(R[i])表示这个字符串的最长回文半径,但是这个时候出现了(bug):
(ykyyky)
(ykykyky)
对于前一个子串,是偶数回文子串,而后一个回文子串是奇数回文子串。这个时候我们该怎么表示它们回文半径的差别?(3)和(3.5)?✘ 这个时候我们可以在每个字符串之间加‘(#’)
(#y#k#y#y#k#y#)
(#y#k#y#k#y#k#y#)
于是这样它们的回文半径就唯一确定了;
看处理:(R[i])表示最长回文半径,当我们求得每个位置的(R[i]),当加了('#')之后,(R[i]_{max}-1)就是我们要求的最长回文串长度(感性 举例李姐
怎么处理?
求(R[i])
设前(i-1)个数中的回文串的右端点的最大值为(r),取得最大右端点的数为(mid)。显然(r=mid+R[mid])
(mathfrak{A}.)(ileq r)
计算(i)关于(mid)的对称点(j=mid*2-i),
(mathfrak{a}.)(j-R[j]>mid-R[mid]),即(i)的对称点的回文串的范围包含在(mid)对应点的回文串范围,那么(i)的回文串和(j)的回文串一定是对称分布的(因为(i、j)关于(mid)对称并且在(mid)的回文半径内),则(R[i]=R[j])
(mathfrak{b}.) (j-R[j]leq mid-R[mid]),则此时关于(i、j)关于(mid)对称分布并且在(mid)回文半径内的一定是对称的,但是在回文半径之外是否对称我们不清楚,因此我们用最简单粗暴的办法:暴力拓展;
(mathfrak{B}.i>r)
于是暴力拓展√
在每次完成以上三项后,尝试更新(r、mid):
if(r<i+R[i]) {
r=i+R[i]-1;
mid=i;
}
然后复杂度不会证,(一定是我太菜了.
(Code:)
码量不是很大,注意字符串头尾都要插入一个('#')
#include<bits/stdc++.h>
using namespace std;
char s[22000703];
int R[22000703],len;
void read() {
char ch=getchar();
s[0]='~';s[++len]='#';
while(ch>'z'||ch<'a') ch=getchar();
while(ch>='a'&&ch<='z') s[++len]=ch,s[++len]='#',ch=getchar();
}
int main () {
read();
int r=0,mid=0,ans=0;
for(int i=1;i<=len;i++) {
if(i<=r) R[i]=min(R[2*mid-i],r-i+1);
while(s[i-R[i]]==s[i+R[i]]&&s[i-R[i]]!='~') ++R[i];
if(r<i+R[i]) {
r=i+R[i]-1;
mid=i;
}
ans=max(ans,R[i]);
}
printf("%d",ans-1);
return 0;
}