LG4287双倍回文(Manacher)
LG4287双倍回文
解题思路
据说本体有很多乱搞方法,但是可以用 \(O(n)\) 得 manacher
解决。
我们按照正常的 manacher
做,我们要验证 \(i\) 这个位置是否可以为右半回文串的中心位置。由于右半回文串长度为偶数,所以中心位置只可能为 #
字符。我们找到位置 \(i\) 关于 \(mid\) 的对称点 \(j\)。如果以 \(i\) 为中心的最长回文串和以 \(j\) 为中心的最长回文串有交集,那么 \([j,i]\) 这一段子串去掉 #
就可以形成一个所求的字符串。
我们注意到,还有一些限制条件,例如 \(mid\) 是 #
字符。所以我们可以考虑只对为 #
的字符跑 manacher
。
时间复杂度为 \(O(n)\)。实测跑得飞快。
代码
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
char read_char() {
char ch=getchar();
while(!isalpha(ch)) {
ch=getchar();
}
return ch;
}
const int MAXN=1e6+10;
int n,p,mx,m;
int r[MAXN],c[MAXN];
int main() {
cin>>n;
c[++m]=27;
for(int i=1;i<=n;i++) {
c[++m]=read_char()-'a'+1;
c[++m]=27;
}
int mid=0,mx=0,ans=0;
for(int i=1;i<=m;i+=2) {
if(i>mx) {
r[i]=1;
}
else {
r[i]=min(r[mid*2-i],mx-i+1);
}
if(i-r[i]+1<=mid&&i<=mx) {
ans=max(ans,(i-mid)*2);
}
while(i-r[i]>=1&&i+r[i]<=m&&c[i+r[i]]==c[i-r[i]]) {
r[i]++;
}
if(i+r[i]-1>mx) {
mid=i;
mx=i+r[i]-1;
}
}
cout<<ans<<endl;
return 0;
}