luogu P5446 [THUPC2018]绿绿和串串
题面传送门
看上去像THUPC的签到题。
首先考虑一个\(O(n\ln n)\)的暴力,就是先枚举一个长度,然后往后暴力倍增,如果不能倍增就直接跳出。如果能到序列末尾那么这个东西就是可行的。
如何判可行大概就是manacher求个回文就好了。
然后发现对于\(i>\frac{n}{2}\)只需要倍增一次,剩下的是除了第一次倍增上去,剩下的都是\(2i-1\)的重复计算。然后就做到\(O(n)\)了。
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define RI re int
#define ll long long
#define db double
#define lb long db
#define N (2000000+5)
#define M (500000+5)
#define mod 1000000007
#define Mod (mod-1)
#define eps (1e-9)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int T,n,F[N+5],H,Fl[N+5],Mid,Len;char S[N+5],A[N+5];
I void Solve(){
RI i;scanf("%s",S+1);n=strlen(S+1);if(n==1){printf("1\n");return;}A[H=1]='#';for(i=1;i<=n;i++) A[++H]='.',A[++H]=S[i];A[++H]='.';A[++H]='$';
for(i=1;i<=H;i++) F[i]=1;Mid=1;Len=1;for(i=2;i<H;i++){
i<=Mid+Len-1&&(F[i]=min(F[Mid*2-i],Mid+Len-i));while(A[i-F[i]]==A[i+F[i]]) F[i]++;i+F[i]>Mid+Len&&(Mid=i,Len=F[i]);
}for(i=1;i<=n;i++) Fl[i]=0;for(i=n;i>1;i--) i*2>n?(F[i*2+1]/2==n-i+1&&(Fl[i]=1)):(Fl[i*2-1]&&F[i*2+1]/2==i&&(Fl[i]=1));for(i=2;i<=n;i++) Fl[i]&&(printf("%d ",i));printf("\n");
}
int main(){
freopen("1.in","r",stdin);
scanf("%d",&T);while(T--) Solve();
}