依旧是后面再补上讲解吧希望我不要忘记惹。。。。
本质是一颗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 }
- 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 }
- 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 }