AC自动机
板子题.
AC自动机的建立(指针版本,数组版本附在注释后面)
fail树
const int M=26,N=1010000,mod=1e9+7;
struct node{
node *son[M],*go[M],*fail;//go[i]表示当前节点位置匹配i时,最终会跳到哪个节点才能匹配成功,fail就类似于KMP的next数组
}pool[N],*cur=pool,*root,*d[210];//cur表示当前指向pool的位置
node *newnode(){//新创立一个节点
return cur++;
}
node *q[N];
void build(){
int t=0;
q[t++]=root;//bfs序更新每个节点的fail以及go(这个信息的更新位置顺序就是按照深度从小到大,即bfs序),q[t++]=0;
for(int i=0;i<t;i++){
node *u=q[i];
for(int j=0;j<M;j++){
if(u->son[j]){//如果说存在这个儿子的话
u->go[j]=u->son[j];//在u这个位置,如果要匹配j的话,正好u的下一个有j,那么肯定是go[u][j]=son[u][j]最优的
if(u==root) u->son[j]->fail=root;//他儿子的fail肯定等于他本身,即根节点,fail[son[u][j]]=root;
else u->son[j]->fail=u->fail->go[j];//否则他儿子的fail就等于他自己的fail然后再找下一个能匹配到的,fail[son[u][j]]=go[fail[u]][j]
/*这一步就相当于求next数组,能肯定的是u要先退到next[u],然后在跟son[u][j]匹配 */
q[t++]=u->son[j];//q[t++]=son[u][j]
}else {
if(u==root)u->go[j]=root;//go[u][j]=root;
else u->go[j]=u->fail->go[j]; //go[u][j]=go[fail[u]][j];
}
}
}
}
void solve() {
int n;cin>>n;
root=newnode();//字典树的根节点
for(int i=0;i<n;i++){
string t;cin>>t;
int m=t.size();
node *p=root;//每次从根节点往下走,int p=0;
for(int j=0;j<m;j++){
int w=t[j]-'a';
if(!p->son[w])p->son[w]=newnode();//如果说没有这个节点,就新建一个,son[p][w]=++idx;
p=p->son[w];//p=son[p][w];
}
d[i]=p;//记录每个单词都走到了字典树上的哪个位置,d[i]=p;
}
build();//建立ACAM
}
AC代码
#include <bits/stdc++.h>
#define int long long
#define x first
#define y second
#define endl '\n'
using namespace std;
const int M=26,N=1010000,mod=1e9+7;
struct node{
node *son[M],*go[M],*fail;
int cnt;//表示这个节点经过了多少次
}pool[N],*cur=pool,*root,*d[210];
node *newnode(){
return cur++;
}
node *q[N];
char tt[N];
string s;
int t=0;
void build(){
q[t++]=root;
for(int i=0;i<t;i++){
node *u=q[i];
for(int j=0;j<M;j++){
if(u->son[j]){
u->go[j]=u->son[j];
if(u==root) u->son[j]->fail=root;
else u->son[j]->fail=u->fail->go[j];
q[t++]=u->son[j];
}else {
if(u==root)u->go[j]=root;
else u->go[j]=u->fail->go[j];
}
}
}
}
void solve() {
int n;cin>>n;
root=newnode();
for(int i=0;i<n;i++){
cin>>tt;
int m=strlen(tt);
node *p=root;
for(int j=0;j<m;j++){
int w=tt[j]-'a';
s.push_back(tt[j]);
if(!p->son[w])p->son[w]=newnode();
p=p->son[w];
}
d[i]=p;
s.push_back(0);//相当于是空格
}
build();
node *p=root;
for(char ch:s){
if(ch==0)p=root;
else p=p->go[ch-'a'];
p->cnt++;
}
//树形dp,求每个串的出现次数
for(int i=t-1;i>=1;i--){//默认深度从大到小的顺序更新
q[i]->fail->cnt+=q[i]->cnt;
}
for(int i=0;i<n;i++){
cout<<d[i]->cnt<<endl;
}
}
signed main() {
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int T=1;
// cin>>T;
while (T--) solve();
}