AC 自动机
AC 自动机
AC 自动机是 以 Trie 的结构为基础,结合 KMP 的思想 建立的。
简要步骤
- 将所有的模式串构成一棵
。 - 对
树上所有的结点构造失配指针。
然后就可以利用它进行多模式串匹配了。
建 树
il void ins(char s[]){
ri int now=0,len=strlen(s); // 一定要记录len,不然会T
for(ri int i=0,o;o=s[i]-'a',i<len;++i){
if(!t[now].nxt[o]) t[now].nxt[o]=++id;
now=t[now].nxt[o];
}
return operate(); // 一些操作,如t[now].cnt++
}
构造失配指针
il void getfail(){
ri int now=0;
for(ri int i=0;i<26;++i){
if(t[now].nxt[i]) q.push(t[now].nxt[i]);
}
while(!q.empty()){
now=q.front(),q.pop();
for(ri int i=0;i<26;++i){
if(t[now].nxt[i]){
t[t[now].nxt[i]].fail=t[t[now].fail].nxt[i];
q.push(t[now].nxt[i]);
}
else t[now].nxt[i]=t[t[now].fail].nxt[i];
}
}
return;
}
求有多少个不同的模式串在文本串里出现过
il void ins1(char s[]){
ri int now=0,len=strlen(s);
for(ri int i=0,o;o=s[i]-'a',i<len;++i){
if(!t[now].nxt[o]) t[now].nxt[o]=++id;
now=t[now].nxt[o];
}
return t[now].cnt++,void();
}
il int ask1(char s[]){
// 求有多少个不同的模式串在文本串里出现过
ri int as=0,now=0,len=strlen(s);
for(ri int i=0,o;o=s[i]-'a',i<len;++i){
now=t[now].nxt[o];
for(ri int v=now;v&&~t[v].cnt;v=t[v].fail){
as+=t[v].cnt,t[v].cnt=-1;
}
}
return as;
}
code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
using namespace std;
cs int N=1e6+1;
namespace AC_Automaton{
struct trie{
int fail,cnt,nxt[26];
}t[N];
int id;queue<int> q;
il void ins1(char s[]){
ri int now=0,len=strlen(s);
for(ri int i=0,o;o=s[i]-'a',i<len;++i){
if(!t[now].nxt[o]) t[now].nxt[o]=++id;
now=t[now].nxt[o];
}
return t[now].cnt++,void();
}
il void getfail(){
ri int now=0;
for(ri int i=0;i<26;++i){
if(t[now].nxt[i]) q.push(t[now].nxt[i]);
}
while(!q.empty()){
now=q.front(),q.pop();
for(ri int i=0;i<26;++i){
if(t[now].nxt[i]){
t[t[now].nxt[i]].fail=t[t[now].fail].nxt[i];
q.push(t[now].nxt[i]);
}
else t[now].nxt[i]=t[t[now].fail].nxt[i];
}
}
return;
}
il int ask1(char s[]){
// 求有多少个不同的模式串在文本串里出现过
ri int as=0,now=0,len=strlen(s);
for(ri int i=0,o;o=s[i]-'a',i<len;++i){
now=t[now].nxt[o];
for(ri int v=now;v&&~t[v].cnt;v=t[v].fail){
as+=t[v].cnt,t[v].cnt=-1;
}
}
return as;
}
} using namespace AC_Automaton;
signed main(){
ri int n;char s[N];scanf("%d",&n);
while(n--) scanf("%s",s),ins1(s);
getfail(),scanf("%s",s),printf("%d",ask1(s));
return 0;
}
求模式串在文本串中出现次数(小数据)
il void ins2(char s[],int tot){
ri int now=0,len=strlen(s);
for(ri int i=0,o;o=s[i]-'a',i<len;++i){
if(!t[now].nxt[o]) t[now].nxt[o]=++id;
now=t[now].nxt[o];
}
return t[now].cnt=tot,void();
}
il void ask2(char s[],int n){
// 求出现次数
for(ri int i=2;i<=n;++i) as[i]=Id[i]=0;
tot=as[1]=0,Id[0]=1;
ri int now=0,len=strlen(s);
for(ri int i=0,o;o=s[i]-'a',i<len;++i){
now=t[now].nxt[o];
for(ri int v=now;v;v=t[v].fail){
as[t[v].cnt]++;
}
}
return;
}
code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
using namespace std;
cs int N=1e6+1,T=151,L=71;
namespace AC_Automaton{
struct trie{
int fail,cnt,nxt[26];
}t[N];
int id;queue<int> q;
il void ins2(char s[],int tot){
ri int now=0,len=strlen(s);
for(ri int i=0,o;o=s[i]-'a',i<len;++i){
if(!t[now].nxt[o]) t[now].nxt[o]=++id;
now=t[now].nxt[o];
}
return t[now].cnt=tot,void();
}
il void getfail(){
ri int now=0;
for(ri int i=0;i<26;++i){
if(t[now].nxt[i]) q.push(t[now].nxt[i]);
}
while(!q.empty()){
now=q.front(),q.pop();
for(ri int i=0;i<26;++i){
if(t[now].nxt[i]){
t[t[now].nxt[i]].fail=t[t[now].fail].nxt[i];
q.push(t[now].nxt[i]);
}
else t[now].nxt[i]=t[t[now].fail].nxt[i];
}
}
return;
}
il void clr(){
for(ri int i=0;i<=id;++i){
memset(t[i].nxt,0,sizeof(int)*26);
t[i].fail=t[i].cnt=0;
}
return id=0,void();
}
int as[T],Id[T],tot;
il void ask2(char s[],int n){
// 求哪些模式串在文本串中出现的次数最多
for(ri int i=2;i<=n;++i) as[i]=Id[i]=0;
tot=as[1]=0,Id[0]=1;
ri int now=0,len=strlen(s);
for(ri int i=0,o;o=s[i]-'a',i<len;++i){
now=t[now].nxt[o];
for(ri int v=now;v;v=t[v].fail){
as[t[v].cnt]++;
}
}
for(ri int i=2;i<=n;++i){
if(as[i]==as[Id[0]]) Id[++tot]=i;
else if(as[i]>as[Id[0]]) Id[tot=0]=i;
}
return;
}
} using namespace AC_Automaton;
signed main(){
ri int n;char s[T][L],c[N];scanf("%d",&n);
while(n){
for(ri int i=1;i<=n;++i){
scanf("%s",s[i]),ins2(s[i],i);
}
getfail(),scanf("%s",c),ask2(c,n);
printf("%d\n",as[Id[0]]);
for(ri int i=0;i<=tot;++i){
printf("%s\n",s[Id[i]]);
}
clr(),scanf("%d",&n);
}
return 0;
}
求模式串在文本串中出现次数(大数据)
il void ins3(char s[],int tot){
ri int now=0,len=strlen(s);
for(ri int i=0,o;o=s[i]-'a',i<len;++i){
if(!t[now].nxt[o]) t[now].nxt[o]=++id;
now=t[now].nxt[o];
}
return Id[tot]=now,void();
// 记录每个模式串在 Trie 树上的终止节点
}
il void ask3(char s[],int n){
// 求模式串在文本串中出现的次数
ri int now=0,len=strlen(s);
for(ri int i=0,o;o=s[i]-'a',i<len;++i){
now=t[now].nxt[o];
as[now]++;
}
return;
}
优化
暴力跳
考虑建出
namespace qwq{ //优化
int h[N];
struct edge{int v,nxt; }e[N];
il void add(int u,int v,int i){
e[i]={v,h[u]},h[u]=i;
}
il void rbuild(){
for(ri int i=1;i<=id;++i){
add(t[i].fail,i,i);
}
}
il void dfs(int u){
for(ri int i=h[u];i;i=e[i].nxt){
dfs(e[i].v),as[u]+=as[e[i].v];
}
return;
}
} using namespace qwq;
code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
using namespace std;
cs int N=2e6+1,T=2e5+1;
namespace AC_Automaton{
struct trie{
int fail,nxt[26];
}t[N];
int id,Id[T];queue<int> q;
il void ins3(char s[],int tot){
ri int now=0,len=strlen(s);
for(ri int i=0,o;o=s[i]-'a',i<len;++i){
if(!t[now].nxt[o]) t[now].nxt[o]=++id;
now=t[now].nxt[o];
}
return Id[tot]=now,void();
}
il void getfail(){
ri int now=0;
for(ri int i=0;i<26;++i){
if(t[now].nxt[i]) q.push(t[now].nxt[i]);
}
while(!q.empty()){
now=q.front(),q.pop();
for(ri int i=0;i<26;++i){
if(t[now].nxt[i]){
t[t[now].nxt[i]].fail=t[t[now].fail].nxt[i];
q.push(t[now].nxt[i]);
}
else t[now].nxt[i]=t[t[now].fail].nxt[i];
}
}
return;
}
int as[T];
il void ask3(char s[],int n){
// 求模式串在文本串中出现的次数
ri int now=0,len=strlen(s);
for(ri int i=0,o;o=s[i]-'a',i<len;++i){
now=t[now].nxt[o];
as[now]++;
}
return;
}
} using namespace AC_Automaton;
namespace qwq{ //优化
int h[N];
struct edge{int v,nxt; }e[N];
il void add(int u,int v,int i){
e[i]={v,h[u]},h[u]=i;
}
il void rbuild(){
for(ri int i=1;i<=id;++i){
add(t[i].fail,i,i);
}
}
il void dfs(int u){
for(ri int i=h[u];i;i=e[i].nxt){
dfs(e[i].v),as[u]+=as[e[i].v];
}
return;
}
} using namespace qwq;
signed main(){
ri int n;char s[N];scanf("%d",&n);
for(ri int i=1;i<=n;++i){
scanf("%s",s),ins3(s,i);
}
getfail(),scanf("%s",s),ask3(s,n);
rbuild(),dfs(0);
for(ri int i=1;i<=n;++i){
printf("%d\n",as[Id[i]]);
}
return 0;
}
I went to the woods because I wanted to live deliberately, I wanted to live deep and suck out all the marrow of life, and not when I had come to die, discover that I had not live.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现