manacher
用以处理的问题形式
最简单的应用,求一个串中的最长回文串。
算法流程
Part 1 预处理
将两种不同的回文串 \(\cdots B \cdots\) 和 \(\cdots BB \cdots\) 合并成一种情况来考虑。
在两两字符之间插入一个特殊符号,这样就无需考虑究竟是哪一种对称了。
Part 2 主体部分
这里依然是非常像 KMP 和 扩展 KMP 的,大力利用之前已有的信息,然后暴力扩展,并且拥有 \(\operatorname O(n)\) 的优秀复杂度。
利用对称的性质,记录下当前的右端点最远的回文串,然后可以利用这个串得到当前位置 \(i\) 的一个初始值,从而减少计算。
讲详细要画图,但我不想画,读者自悟不难。
int main()
{
int i,j;
cin>>a;n=a.length();
for(i=0;i<n;i++)b[(i<<1)+1]=a[i],b[i<<1]='#';b[n<<1]='#';
int lst=0;l[0]=1;
int ans=0;
for(n<<=1,i=1,j=1;i<=n;i++)
{
if((lst<<1)>=i)l[i]=min(j-i,l[(lst<<1)-i]);if(!l[i])l[i]++;
for(;i+l[i]<=n&&i>=l[i]&&b[i+l[i]]==b[i-l[i]];l[i]++);
// printf("%d ",l[i]);
if(i+l[i]>j)j=i+l[i],lst=i;
ans=max(ans,(b[i]=='#')?(l[i]-1):((l[i]>>1)-1<<1)+1);
}
printf("%d\n",ans);
return 0;
}
复杂度证明
在第二个步骤中,那个 for 循环每次跑一遍的时候 必定更新最大右端点,所以复杂度可以均摊,是 \(\operatorname O(n)\) 的。
$$\texttt{Dirty Deeds Done Dirt Cheap}$$