[説明] P5446 [THUPC2018]と緑とストリング(manacher)
文字列のために考えてみましょう\(F \)の操作、それは最後のノードの回文センターに回文文字列に彼を作ることです。
次いで、位置のために\(P \)が有効な場所であれば長さは、元の文字列を行う場合倍以上である場合、それは二倍にされている\は(T \)一度現れました。
- ストリング回元の長さよりも大きい時間が、その後\(Pは\)中心パリンドロームであり、\(P \)パリンドローム半径に達する\(| T | \)
- 、その後、ストリングの元の長さよりもわずか2回\(S \)点倍加時間こと\(pは「\)中心パリンドロームであり、\(P」\)に達したパリンドローム半径\(| T | \) 。しかし、場合に元の第1の乗算器の正当性を保証するために、\(P \)この耐えるドローム半径位置\(1 \)ノード。
- 三回限り...
場合は、再帰的な法的地位を考えると、場合にのみ、この位置は、右のボーダーのトップ、あるいは自分自身の正当なの右の境界に回文半径で、上部の境界線に半径をお願いします。
Manacherは、細部へのこだわりを実装することができ、各文字が非補助文字用のカートを描かれ、彼は二文字でなければなりません回文回文境界文字列の中心だった、減算する\(1 \)を所望の効果を達成するために、 。
複雑\(O(2 \総和| S |)\)
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; typedef long long ll;
inline int qr(){
register int ret=0,f=0;
register char c=getchar();
while(c<48||c>57)f|=c==45,c=getchar();
while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=1e6+5;
char s[maxn<<1];
bool f[maxn<<1];
int p[maxn<<1],cnt,n;
inline void manacher(){
s[0]='~',s[cnt=1]='|';
char c=getchar();
while(c<'a'||c>'z') c=getchar();
while(c>='a'&&c<='z') s[++cnt]=c,s[++cnt]='|',c=getchar();
s[cnt+1]='\0';
for(int t=1,r=0,mid=0;t<=cnt;++t){
p[t]=0;
if(r>=t) p[t]=min(r-t+1,p[(mid<<1)-t]);
while(s[t-p[t]]==s[t+p[t]]) ++p[t];
if(t+p[t]-1>r) r=t+p[t]-1,mid=t;
}
}
int main(){
n=qr();
for(int t=1;t<=n;++t){
manacher();
f[cnt]=1;
for(int t=cnt;t;--t) f[t]=0,f[t]=(t+p[t]-1==cnt)||(t-p[t]+1==1&&f[t+p[t]-2]);
for(int t=2;t<=cnt;t+=2) if(f[t]) printf("%d ",t>>1);
putchar('\n');
}
return 0;
}