1. 程式人生 > >【學習筆記】迴文自動機

【學習筆記】迴文自動機

依舊是後面再補上講解吧希望我不要忘記惹。。。。

本質是一顆trie,節點代表了迴文的一半;

自動機節點維護長度和最大回文字尾的fail指標;

奇數迴文的根長度為-1,編號1,偶數0,1;(編號是有藝術的)

插入沿著上一個點找是否有匹配的迴文;

注意如果要新建節點的話應該再找新建點的fail,繼續想上跳即可找到,此時要先找fail再連新建點,不然在新建點的父親為1的時候會出錯;

 

  • 習題:

    • bzoj2565最長雙迴文串 
    • 用迴文自動機正反兩邊跑出每個點的前後最長迴文串,列舉斷點統記ans
    •  1 #include<bits/stdc++.h>
       2 #define rg register
       3 #define il inline
       4 using namespace std;
       5 const int N=100010;
       6 char s[N];
       7 int n,sz,fl[N],len[N],ch[N][26],l[N],r[N];
       8 il int find(int x,int y){
       9     return s[y]==s[y-len[x]-1]?x:find(fl[x],y);
      10 }
      11 void cal(){
      
      12 memset(ch,0,sizeof(ch)); 13 memset(fl,0,sizeof(fl)); 14 sz=1; 15 fl[0]=1;fl[1]=1; 16 len[0]=0;len[1]=-1; 17 for(rg int i=1,now;i<=n;i++){ 18 now = find(now,i); 19 if(!ch[now][s[i]-'a']){ 20 len[++sz]=len[now]+2; 21 fl[sz]=ch[find(fl[now],i)][s[i]-'
      a']; 22 ch[now][s[i]-'a']=sz; 23 } 24 now=ch[now][s[i]-'a']; 25 r[i] = len[now]; 26 } 27 } 28 int main(){ 29 freopen("bzoj2565.in","r",stdin); 30 freopen("bzoj2565.out","w",stdout); 31 s[0] = -1; 32 scanf("%s",s+1); 33 n=strlen(s+1); 34 cal(); 35 for(rg int i=1;i<=n;i++)l[i] = r[i]; 36 for(rg int i=1;i<=n>>1;i++) 37 swap(s[i],s[n-i+1]); 38 cal(); 39 int ans = 0; 40 for(rg int i=1;i<n;i++){ 41 ans = max(ans, l[i] + r[n - i]); 42 } 43 cout << ans << endl; 44 return 0; 45 }
      bzoj2565
    • bzoj3676[Apio2014]迴文串
    • 迴文自動機裡節點儲存狀態是本質不同的迴文串;
    • 記錄每個節點更新的次數$cnt[]$ , 一個節點出現同時代表其fail祖先都出現,所以$cnt[ fl[i] ] +=  cnt[i]$;
    • 有個技巧:迴文自動機的拓撲序即編號序列;
       1 #include<bits/stdc++.h>
       2 #define rg register
       3 #define il inline
       4 #define Run(i,l,r) for(rg int i=l;i<=r;i++)
       5 #define Don(i,l,r) for(rg int i=l;i<=r;i++)
       6 #define ll long long
       7 using namespace std;
       8 const int N=300100;
       9 int n,fl[N],ch[N][26],tot,len[N],sz; 
      10 ll cnt[N];
      11 char s[N];
      12 inline int find(int x,int y){
      13     while(1){
      14         if(s[x - len[y] - 1] == s[ x ])break;
      15         y = fl[ y ];
      16     } 
      17     return y;
      18 }
      19 int main(){
      20     freopen( "bzoj3676.in" , "r" , stdin);
      21     freopen( "bzoj3676.out", "w", stdout);
      22     scanf("%s",s+1);
      23     sz=1;
      24     fl[0]=fl[1]=1;
      25     len[0]=0;len[1]=-1;
      26     int now = 0 ;
      27     s[0] = -1;
      28     for(n=1 ; s[ n ] ; n++){
      29         s[ n ] -= 'a' ;
      30         now = find( n , now );
      31         if(! ch[ now ][ s[n] ]){
      32             len[ ++sz ] = len[now] + 2; 
      33             int tmp = find( n , fl[ now ] );
      34             fl[ sz ] = ch[ tmp ][ s[n] ];
      35             ch[ now ][ s[n] ] = sz ; 
      36         }
      37         cnt[ now = ch[ now ][ s[n] ] ] ++;
      38     }
      39     ll ans = 0;
      40     for( int i = sz; i > 1 ; --i){
      41         cnt[ fl[i] ] += cnt[ i ];
      42         ans = max(ans , cnt[i] * len[i] );
      43     }
      44     cout << ans << endl;
      45     return 0;
      46 }
      bzoj3676
    • bzoj4480[Jsoi2013]快樂的jyy
    • 迴文串出現次數的乘積的和的話。。。
    • 先對$A$串做迴文自動機,然後$hash$存下每個迴文串出現次數
    • 然後對$B$串再做一次統計答案
    • $hash$注意區分奇偶,同時每位最小值不要出現0 ,$fxy$說常見模數+底$26$是被卡了的($推薦2333333$)
       1 #include<cstdio>
       2 #include<iostream>
       3 #include<map>
       4 #include<cstring>
       5 #define rg register
       6 #define il inline 
       7 #define ull unsigned long long
       8 #define ll long long
       9 #define base 233
      10 using namespace std;
      11 const int N=50010;
      12 ull h[N];
      13 char s[N];
      14 ll cnt[N],ans;
      15 int n,sz,fl[N],ch[N][26],len[N],db[N];
      16 map<ull,ll>mp;
      17 il int newd(int d){
      18     len[sz]=d;
      19     fl[sz]=cnt[sz]=h[sz]=0;
      20     memset(ch[sz],0,sizeof(ch[sz]));
      21     return sz++;
      22 }
      23 void init(){
      24     s[0]=-1;
      25     sz=0;
      26     newd(0);newd(-1);
      27     fl[0]=fl[1]=1;
      28     h[0]=0;h[1]=-1;
      29 }
      30 il int find(int x,int y){return s[x]==s[x-len[y]-1]?y:find(x,fl[y]);}
      31 void solve(int fg){
      32     init();
      33     int l = strlen(s+1);
      34     for(rg int i=1,now=0;i<=l;i++){
      35         now = find(i,now);
      36         if(!ch[now][s[i]-'A']){
      37             fl[newd(len[now]+2)]=ch[find(i,fl[now])][s[i]-'A'];
      38             ch[now][s[i]-'A']=sz-1;
      39             h[sz-1]=h[now]*base+s[i];
      40         }
      41         now = ch[now][s[i]-'A'];
      42         cnt[now]++;
      43         db[i] = len[now];
      44     }
      45     for(rg int i=sz-1;i>=2;i--)if(!fg){
      46         cnt[fl[i]]+=cnt[i];
      47         mp[h[i]] = cnt[i];
      48     }else{
      49         cnt[fl[i]]+=cnt[i];
      50         ans += mp[h[i]] * cnt[i];
      51     }
      52 }
      53 int main(){
      54     freopen("bzoj4480.in","r",stdin);
      55     freopen("bzoj4480.out","w",stdout);
      56     scanf("%s",s+1);solve(0);
      57     scanf("%s",s+1);solve(1);
      58     printf("%lld\n",ans);
      59     return 0;
      60 }
      bzoj4480