1. 程式人生 > >BZOJ 2342 [SHOI2011]雙倍迴文 (迴文自動機)

BZOJ 2342 [SHOI2011]雙倍迴文 (迴文自動機)

題目大意:略

先建出$PAM$

因為雙倍迴文串一定是4的倍數,所以找出$PAM$裡所有$dep$能整除4的節點

看這個串是否存在一個迴文字尾,長度恰好為它的一半,沿著$pre$鏈往上跳就行了

暴跳可能會$T$,所以倍增了跳

如果被卡空間,可以把trs陣列當成倍增陣列

 1 #include <cmath>
 2 #include <vector>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <algorithm>
 6 #define N1 500100
 7
#define S1 (N1<<1) 8 #define ll long long 9 #define uint unsigned int 10 #define rint register int 11 #define dd double 12 #define il inline 13 #define inf 0x3f3f3f3f 14 #define idx(X) (X-'a') 15 using namespace std; 16 17 int gint() 18 { 19 int ret=0,fh=1;char c=getchar();
20 while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();} 21 while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();} 22 return ret*fh; 23 } 24 int len; 25 namespace PAM{ 26 int trs[N1][26],pre[N1],dep[N1],la,tot; 27 void init(){la=tot=1,pre[0]=pre[1]=1,dep[1]=-1;} 28 int chk(char
*str,int i,int p){return str[i-dep[p]-1]!=str[i]?1:0;} 29 void insert(char *str,int i) 30 { 31 int p=la,np,fp,c=idx(str[i]); 32 while(chk(str,i,p)) p=pre[p]; 33 if(!trs[p][c]) 34 { 35 np=++tot; 36 dep[np]=dep[p]+2; 37 fp=pre[p]; 38 while(chk(str,i,fp)) fp=pre[fp]; 39 pre[np]=trs[fp][c]; 40 trs[p][c]=np; 41 } 42 la=trs[p][c]; 43 } 44 int multi() 45 { 46 int ans=0,i,j,x; 47 trs[1][0]=trs[1][1]=0; 48 trs[0][0]=trs[0][1]=0; 49 for(i=2;i<=tot;i++) trs[i][0]=i,trs[i][1]=pre[i]; 50 for(j=2;j<=20;j++) 51 for(int i=2;i<=tot;i++) 52 trs[i][j]=trs[ trs[i][j-1] ][j-1]; 53 for(i=2;i<=tot;i++) 54 if(dep[i]%4==0) 55 { 56 x=i; 57 for(j=20;j>=0;j--) 58 if(dep[trs[x][j]]>=dep[i]/2) x=trs[x][j]; 59 if(dep[x]!=dep[i]/2) continue; 60 ans=max(ans,dep[i]); 61 } 62 return ans; 63 } 64 }; 65 char str[N1]; 66 67 int main() 68 { 69 //freopen("t2.in","r",stdin); 70 //freopen("a.out","w",stdout); 71 scanf("%d",&len); 72 scanf("%s",str+1); 73 PAM::init(); 74 for(int i=1;i<=len;i++) PAM::insert(str,i); 75 printf("%d\n",PAM::multi()); 76 return 0; 77 }