Manacher
Manacher 学习笔记
问题
求出串 \(s\) 中最长回文串的长度。
分析
数据范围不大的情况下,估计还是可以哈希加二分解决。
但是严格线性的算法还是更加优秀的。
字符串这几个基础算法绕来绕去实际上就是一个自动机的思想,我当前想要计算某个答案,于是看看之前的答案是否能有帮助。
首先定义 \(r_i\) 为:以 \(i\) 为中心的最长回文半径是多少。
不过又遇到了问题,就是当回文串长度是偶数的时候该怎么办呢?这样的话,我们在每个空插入一个特殊字符就可以了。比如将 \(\text{abcdef}\) 变成 \(\text{a#b#c#d#e#f}\)。
在递推的同时,我们记录覆盖点最右的一个回文区间,如果当前的 \(i\) 落在了这个回文区间内,说明 \(i\) 的答案就可以从 \(i\) 关于这个区间的对称点 \(2\times mid-i\) 相等地转移过来,这个时候我们再尝试去扩展 \(i\) 的回文区间,由于这个“最右覆盖点”一共最多只会移动 \(n\) 次,所以算法是严格 \(O(n)\) 的。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=1.1e7+10;
char tmp[N<<1],s[N<<1];
int n,r[N<<1];
int maxpos,mid;
inline void pre()
{
scanf("%s",tmp+1);
n=strlen(tmp+1);
for(int i=1;i<=2*n+1;++i)
{
if(i&1)s[i]='#';
else s[i]=tmp[i>>1];
}
n=2*n+1;
}
inline void manacher()
{
int ans=0;
for(int i=1;i<=n;i++)
{
if(i<=maxpos)r[i]=min(maxpos-i+1,r[2*mid-i]);
else r[i]=1;
while(1<=i-r[i]&&i+r[i]<=n&&s[i-r[i]]==s[i+r[i]])r[i]++;
if(i+r[i]-1>maxpos)maxpos=i+r[i]-1,mid=i;
if(s[i+r[i]-1]=='#')ans=max(ans,r[i]-1);
else ans=max(ans,r[i]);
}
cout<<ans;
}
int main()
{
pre();
manacher();
return 0;
}
为什么要练,为什么要写?
引用一句让我幡然悔悟的话:
“练了不一定写的出来正解,不练一定写不出来正解”
本文来自博客园,作者:Hanggoash,转载请注明原文链接:https://www.cnblogs.com/Hanggoash/p/18822377

浙公网安备 33010602011771号