AC自动机复习
AC 自动机
应用:
- 多模式串匹配
构建
- 将所有模式串构成一个 Trie 树
- 树上的每个节点代表某一个(或多个)模式串的前缀
- 每个节点同时有一个失配指针,指向当前节点所代表的的串可以匹配到的最长前缀,这个前缀还必须是这个串的后缀。类似于的数组,但的数组是对于一个串而言,在 AC 自动机里一个节点的指针不一定指向自己的前缀,可以是其他模式串的前缀。
- 寻找指针,可以假设比这个极点高度低的极点的都寻找完了,所以我们是通过的方式去寻找指针
- 根据题目的不同,每个节点额外记录的信息也不同,比如每个节点可以表示的模式串个数,每个节点表示的模式串的编号等等
模板
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
namespace AC
{
int tr[N][26],tot;
int fail[N],cnt[N]; //cnt[i]表示第i个节点可以表示的模式串个数
void insert(char *s){ //构建Trie树
int u=0;
for(int i=0;s[i];i++){
int x=s[i]-'a';
if(!tr[u][x]) tr[u][x]=++tot;
u=tr[u][x];
}
cnt[u]++;
}
void build(){
queue<int>q;
for(int i=0;i<26;i++) //先把所有0号节点可以到达的点入队
if(tr[0][i]) q.push(tr[0][i]);
while(q.size()){
int u=q.front();
q.pop();
for(int i=0;i<26;i++){
int &x=tr[u][i];
if(x){
fail[x]=tr[fail[u]][i];
q.push(x);
}else{
x=tr[fail[u]][i];
}
}
}
}
}
char s[N],t[N];
int main()
{
//ios::sync_with_stdio(false);
//cin.tie(nullptr);
int n;
scanf("%d",&n);
for(int i=0;i<n;i++) {
scanf("%s",s);
AC::insert(s);
}
AC::build();
}
应用
1.多模式串匹配 1
Problem
多模式串匹配:给定一个文本串和多个模式串,问有多少个模式串在文本串中出现过
Solve
每个节点记录表示的模式串个数,然后在 Trie 树上沿着文本串走,当走到一个节点假设这个节点是从转移过来的,那么沿着这个节点跳,因为若存在其他模式串并且是以结尾的,那么在当前文本串走过的路径形成的字符串中,若在中出现,则一定是的一个后缀,然后我们对每个跳过的节点标记,这样对于相同的就不会重复了。
Code
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
namespace AC
{
int tr[N][26],tot;
int fail[N],cnt[N]; //cnt[i]表示第i个节点可以表示的模式串个数
void insert(char *s){ //构建Trie树
int u=0;
for(int i=0;s[i];i++){
int x=s[i]-'a';
if(!tr[u][x]) tr[u][x]=++tot;
u=tr[u][x];
}
cnt[u]++;
}
void build(){
queue<int>q;
for(int i=0;i<26;i++) //先把所有0号节点可以到达的点入队
if(tr[0][i]) q.push(tr[0][i]);
while(q.size()){
int u=q.front();
q.pop();
for(int i=0;i<26;i++){
int &x=tr[u][i];
if(x){
fail[x]=tr[fail[u]][i];
q.push(x);
}else{
x=tr[fail[u]][i];
}
}
}
}
int query(char *t){
int u=0,res=0;
for(int i=0;t[i];i++){
u=tr[u][t[i]-'a'];
for(int j=u;j&&cnt[j]!=-1;j=fail[j]){
res+=cnt[j],cnt[j]=-1;
}
}
return res;
}
}
char s[N],t[N];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++) {
scanf("%s",s);
AC::insert(s);
}
AC::build();
scanf("%s",t);
printf("%d\n",AC::query(t));
}
2.多模式串匹配 2
Problem
给定多个模式串和一个文本串,问哪些模式串在文本串中出现的次数最多,并输出这些模式串
Solve
由于不会有两个模式串相同,所以可以给每个节点记录一个可以表示的模式串的编号。然后沿着在 Trie 上走,每次到达一个节点就跳,并且记录每个节点是通过跳到达的次数,最后查询哪些可以表示字符串的节点可以被跳到的最多次数即可。
Code
#include <bits/stdc++.h>
using namespace std;
const int N=155;
namespace AC
{
const int NN=N*80;
int tr[NN][26],tot;
int fail[NN],cnt[NN],id[NN]; //cnt[i]表示第i个节点可以表示的模式串个数
int val[NN];
void init(){
tot=0;
memset(tr,0,sizeof tr);
memset(fail,0,sizeof fail);
memset(cnt,0,sizeof cnt);
memset(id,0,sizeof id);
memset(val,0,sizeof val);
}
void insert(char *s,int idx){ //构建Trie树
int u=0;
for(int i=0;s[i];i++){
int x=s[i]-'a';
if(!tr[u][x]) tr[u][x]=++tot;
u=tr[u][x];
}
id[u]=idx;
cnt[u]++;
}
void build(){
queue<int>q;
for(int i=0;i<26;i++) //先把所有0号节点可以到达的点入队
if(tr[0][i]) q.push(tr[0][i]);
while(q.size()){
int u=q.front();
q.pop();
for(int i=0;i<26;i++){
int &x=tr[u][i];
if(x){
fail[x]=tr[fail[u]][i];
q.push(x);
}else{
x=tr[fail[u]][i];
}
}
}
}
int query(char *t){
int u=0,res=0;
for(int i=0;t[i];i++){
u=tr[u][t[i]-'a'];
for(int j=u;j;j=fail[j]) val[j]++;
}
for(int i=0;i<=tot;i++)
if(id[i]) res=max(res,val[i]),cnt[id[i]]=val[i];
return res;
}
}
char s[151][76];
char t[1000005];
int main()
{
int n;
while(scanf("%d",&n),n){
AC::init();
for(int i=1;i<=n;i++){
scanf("%s",s[i]);
AC::insert(s[i],i);
}
AC::build();
scanf("%s",t);
int res=AC::query(t);
printf("%d\n",res);
for(int i=1;i<=n;i++)
if(AC::cnt[i]==res) printf("%s\n",s[i]);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现