ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【模板】AC自动机

2021-10-04 22:02:30  阅读:105  来源: 互联网

标签:AC int 题解 ll fail 自动机 模板


题意

给定 \(n\) 个模式串 \(s_i\) 和一个文本串 \(t\) ,求有多少个不同的模式串在文本串里出现过。
两个模式串不同当且仅当他们编号不同。

题解

AC 自动机,俗称在 Trie 上跑 KMP ,不能否定但也不能完全认同。
本篇题解并不详细,供己用,在板子里打了一点注释。
AC自动机的精髓在于分情况记录的 \(fail\) 数组。由于 \(fail\) 数组中直接把不合法情况连向虚拟节点 \(0\) ,所以查找的时候不需要像 KMP 一样判断当前是否失配。
\(fail\) 的更新大概有两种情况,如下:

for(int i=0;i<26;i++)
{
	if(trie[p][i]) fail[trie[p][i]]=trie[fail[p]][i],q.push(trie[p][i]);
	//如果trie_p节点存在字符i的儿子, 当前点的fail可以用它父亲p的fail数组更新 
	else trie[p][i]=trie[fail[p]][i];
	//如果trie_p节点不存在为字符i的儿子(失配),就直接连向fail[p]的字符i儿子 
	//如果fail[p]没有,那就连向了0 
}

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline 
const int INF = 0x3f3f3f3f,N = 1e6+10;
inline ll read()
{
	ll ret=0;char ch=' ',c=getchar();
	while(!(c>='0'&&c<='9')) ch=c,c=getchar();
	while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
	return ch=='-'?-ret:ret;
}
int n,m;
//头一次封装写函数 
struct AC
{
	int trie[N][27],fail[N],tot=1;
	int cnt[N]; bool vis[N];
	queue<int> q;
	inl void insert(char s[])
	{
		int p=1,len=strlen(s+1);
		for(int i=1;i<=len;i++)
		{
			int ch=s[i]-'a';
			if(!trie[p][ch]) trie[p][ch]=++tot;
			p=trie[p][ch];
		}
		cnt[p]++;
	}
	inl void build()//BFS求出fail数组 
	{
		for(int i=0;i<26;i++) trie[0][i]=1;//把位置0的儿子指向1(即0的儿子是根) 
		q.push(1);
		while(!q.empty())
		{
			int p=q.front(); q.pop();
			for(int i=0;i<26;i++)
			{
				if(trie[p][i]) fail[trie[p][i]]=trie[fail[p]][i],q.push(trie[p][i]);
				//如果trie_p节点存在字符i的儿子, 当前点的fail可以用它父亲p的fail数组更新 
				else trie[p][i]=trie[fail[p]][i];
				//如果trie_p节点不存在为字符i的儿子(失配),就直接连向fail[p]的字符i儿子 
				//如果fail[p]没有,那就连向了0 
			}
		}
	}
	inl int find(char s[])
	{
		int p=1,len=strlen(s+1),ret=0;
		for(int i=1;i<=len;i++)
		{
			int ch=s[i]-'a';
			int now=trie[p][ch];
			while(now)
			{
				if(vis[now]) break; 
				vis[now]=1;//每一个模式串只能计算一次 
				ret+=cnt[now];
				now=fail[now];//不断跳fail数组,直到跳回0 
			}
			p=trie[p][ch];
		}
		return ret;
	}
	inl void clear()
	{
		memset(trie,0,sizeof(trie));
		memset(fail,0,sizeof(fail));
		memset(cnt,0,sizeof(cnt));
		memset(vis,0,sizeof(vis));
		tot=1;
	}
}ac; 
char s[N];
int main()
{
	int T=read();
	while(T--)
	{
		ac.clear();
		n=read();
		for(int i=1;i<=n;i++)
		{
			scanf("%s",s+1);
			ac.insert(s);
		}
		ac.build();
		scanf("%s",s+1);
		printf("%d\n",ac.find(s));
	}
	return 0;
}

标签:AC,int,题解,ll,fail,自动机,模板
来源: https://www.cnblogs.com/conprour/p/15367332.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有