voidBuildFail(){ queue<int>Q; for (int i=0;i<26;++i){ if (trie[0][i]) Q.push(trie[0][i]); } while (Q.size()){ int now=Q.front();Q.pop(); for (int i=0;i<26;++i){ int &child=trie[now][i]; if (child){ fail[child]=trie[fail[now]][i]; Q.push(child); } else { child=trie[fail[now]][i]; } } } }
AC 自动机的基础应用
例题 1
给你 n 个模式串和一个文本串,问多少个模式串在文本串里面出现过(重复不算)。
我们在插入的时候,标记这个节点有多少次作为终止节点(ed 数组):
1 2 3 4 5 6 7 8 9
voidInsert(int id,char *s){ int len=strlen(s),root=0; for (int i=0;i<len;++i){ int c=s[i]-'a'; if (!trie[root][c]) trie[root][c]=++tot; root=trie[root][c]; } ed[root]++,pos[id]=root; }
再来看一下 Query ,我们依次把 s 所含的字符塞进 AC 自动机,每塞进一个字符,发现 fail 链上的所有节点都可能对答案做出贡献,那么我们暴力跳 fail 链即可。
1 2 3 4 5 6 7 8 9 10 11 12 13
intQuery(char *s){ int len=strlen(s),root=0,ans=0; for (int i=0;i<len;++i){ int c=s[i]-'a'; root=trie[root][c]; for (int j=root;ed[j]!=-1;j=fail[j]){ //注意相同只计算一次 //只要一个前缀已经被重复计算,后面的肯定被重复计算 ans+=ed[j],ed[j]=-1; } } return ans; }
vector<int>G[MAXN]; voidAddEdge(int u,int v){ G[u].push_back(v); } voiddfs(int u){ for (int i=0;i<G[u].size();++i){ int v=G[u][i]; dfs(v); sz[u]+=sz[v]; } }
int len=strlen(s),root=0; for (int i=0;i<len;++i){ int c=s[i]-'a'; root=trie[root][c]; sz[root]++; } for (int i=2;i<=tot;++i){ AddEdge(fail[i],i); } dfs(0); for (int i=1;i<=n;++i){ printf("%d\n",sz[pos[i]]); }
voidBuildFail(){ queue<int>Q; for (int i=0;i<2;++i) if (trie[0][i]) Q.push(trie[0][i]); while (Q.size()){ int now=Q.front();Q.pop(); for (int i=0;i<2;++i){ if (trie[now][i]){ fail[trie[now][i]]=trie[fail[now]][i]; ed[trie[now][i]]|=ed[fail[trie[now][i]]];//标记子节点 Q.push(trie[now][i]); } else { trie[now][i]=trie[fail[now]][i]; } } } }
下面,我们只需要一个简单的 dfs 就能解决问题:
1 2 3 4 5 6 7 8 9 10 11 12
int fd=false,vis[MAXN],stk[MAXN]; voiddfs(int u){ if (fd) return ; if (stk[u]) return fd=true,void();//走到了之前经过的节点,找到了环 if (ed[u]||vis[u]) return ;//不能走到标记过的节点,也不能重复经过节点 vis[u]=true; stk[u]=true; dfs(trie[u][0]),dfs(trie[u][1]); stk[u]=false; }
//dep[i]代表节点的深度 int f[MAXN]; voidQuery(char *s){ int len=strlen(s),root=0; for (int i=0;i<len;++i){ int pos=i+1;//pos是正确的下标 int c=s[i]-'a'; root=trie[root][c]; for (int j=root;j;j=fail[j]){ if (ed[j]&&f[pos-dep[j]]){ f[pos]=1; break; } } } }
memset(f,0,sizeof(f)); f[0]=1; scanf("%s",s); Query(s); int l=strlen(s); for (int i=l;i>=0;--i){ if (f[i]){ printf("%d\n",i); break; } }