【AC自動機】背單詞
阿新 • • 發佈:2022-03-16
- 題意:
0 s v:添加價值為v的字串s
1 t:查詢t中含的s的權值和。(不停位置算多次) - 思路:
線上AC自動機。
同學用過一個妙妙子的分塊演算法。
這裡用二進位制分組:通常用作把線上資料結構問題轉離線
即當前有n個串。然後按n的二進位制分成(\(<=log_2n\))個AC自動機分別維護答案。
e.g \(n=7(111)_2\)
此時會有三個AC自動機,分別表示串[1,4],[5,6],[7,7]個數(sz[])分別為\(2^2,2^1,2^0\)
查詢也so easy.就每局每個自動機,在上面查詢。最多查\(log_2n\)次。
修改,每次加入一個串,先建一個只包含它本身sz[]=1的自動機。
這裡我們用stack維護每個自動機資訊。
然後如果最後一個自動機與前一個sz相等。把後兩個自動機合併。
因為每個串最多被合併\(log_2(s的個數m)\) - code:
細節mx_nd[i]表示第i個自動機的最大(最近新增)節點(因為節點是連續加入的)。
每次刪除st[tp]時,把節點數nd也回溯到mx_nd[tp-2](過程中清空刪的節點),然後再合併st[tp-1]與st[tp]
這樣的好處是,空間是線性的。而且這麼寫常數也很小。
#include<bits/stdc++.h> using namespace std; const int N=1e6+5; typedef long long ll; int va[N],sz[N],tp,rt[N],fail[N],go[N][27],nd,len,head[N],L[N],stot,mx_nd[N]; ll val[N],lans; int s[N]; char ch[N]; void Insert(int u,int l,int r,int w) { for(int i=l;i<=r;i++) { int x=s[i]; if(!go[u][x])go[u][x]=++nd; u=go[u][x]; } val[u]+=w; } int Q[N],hd,tl; void gt_fail(int root) { // printf("!%d\n",root); hd=1,tl=0; for(int i=0;i<26;i++) { if(go[root][i]) fail[go[root][i]]=root,Q[++tl]=go[root][i]; else go[root][i]=root; } while(hd<=tl) { int u=Q[hd++];val[u]+=val[fail[u]]; for(int i=0;i<26;i++) { if(go[u][i]) fail[go[u][i]]=go[fail[u]][i],Q[++tl]=go[u][i]; else go[u][i]=go[fail[u]][i]; } } } void _Pass() { while(tp>1&&sz[tp]==sz[tp-1]) { int tmp=nd;nd=mx_nd[tp-2]; while(tmp>nd) {val[tmp]=fail[tmp]=0;for(int i=0;i<26;i++)go[tmp][i]=0;tmp--;} tp--;sz[tp]*=2;rt[tp]=++nd; for(int i=stot;i>=L[tp];i--) Insert(rt[tp],head[i],i==stot?len:head[i+1]-1,va[i]); gt_fail(rt[tp]);mx_nd[tp]=nd; } } void Query() { int ln=strlen(ch); lans=0; for(int i=1;i<=tp;i++) { int u=rt[i]; for(int j=0;j<ln;j++) { int x=ch[j]-'a'; u=go[u][x];lans+=val[u]; // printf("!%c %d %lld\n",ch[j],u,lans); } } printf("%lld\n",lans); } int main() { int n,Type; scanf("%d%d",&n,&Type); for(int i=1;i<=n;i++) { int opt,x;scanf("%d",&opt); if(!opt) { head[++stot]=len+1; scanf("%s%d",ch,&va[stot]); int ln=strlen(ch); for(int j=0;j<ln;j++)s[++len]=Type?((ll)(ch[j]-'a')^lans)%26:ch[j]-'a'; rt[++tp]=++nd;sz[tp]=1;L[tp]=stot; Insert(rt[tp],head[stot],len,va[stot]);mx_nd[tp]=nd;gt_fail(rt[tp]); _Pass(); } else { scanf("%s",ch); int ln=strlen(ch); if(Type)for(int j=0;j<ln;j++) ch[j]=((ll)(ch[j]-'a')^lans)%26+'a'; Query(); } } return 0; }