【学习笔记】回文自动机

依旧是后面再补上讲解吧希望我不要忘记惹。。。。

本质是一颗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

猜你喜欢

转载自www.cnblogs.com/Paul-Guderian/p/10222797.html
今日推荐