AC自功机

AC自动机在字符串匹配中的应用
本文通过一道题目详细介绍了AC自动机(Aho-Corasick算法)在处理多个字符串匹配问题中的作用,强调了AC自动机相比于暴力匹配的优势。首先,解释了AC自动机的核心——fail指针,并通过广搜预处理fail数组。然后,给出了AC自动机的实现代码,展示了如何在文本串中查找模式串并统计出现次数。最后,提醒读者AC自动机是一个高效的算法,但非法的自动AC机是不可取的。

这个算法适用于多个字符串匹配,简单来说就是树上KMP。

以一道例题来解释洛谷P3808

题目描述

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

输入格式

  • 第一行是一个整数,表示模式串的个数n。
  • 第2到第(n+1) 行,每行一个字符串第 (i+1) 行的字符串表示编号为i的模式串si​。
  • 最后一行是一个字符串,表示文本串t。 

输出格式

输出一行一个整数表示答案。 

思路

这是洛谷上的模板题,求模式串在文本串中出现了多少次。如果不用AC自动机,直接暴力去匹配显然会T飞了。那么,怎么优化呢? 

为了便于操作,我们可以把模式串全扔到trie树上(trie树不会的可以看这个

类似于KMP,在每一次失配时不重头开始匹配,而是跳到合适的位置。怎么判断这个合适的位置在哪呢?类似于KMP的next数组,这里引入fail指针。那么它的原理是什么?我们在匹配字符串时,知道的信息只有我们匹配过的部分,所以fail指针就是利用我们匹配过的部分进行判断如果在当前位置失配要跳到哪里。这里给出一个图片,便于理解fail指针。

 显然,指向的部分是当前匹配过的部分的后缀。那么,如何预处理呢?

我们可以利用广搜来处理,把每一个需要处理的节点入队,代码如下。

void getfail()
{
	queue<int>q;
	for(int i=0;i<=25;i++)
	{
		if(trie[0][i])	q.push(trie[0][i]);
	}
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=0;i<=25;i++)
		{
			if(trie[u][i])
			{
				fail[trie[u][i]]=trie[fail[u]][i];
				q.push(trie[u][i]);
			}
			else
			{
				trie[u][i]=trie[fail[u]][i];//路径压缩
			}
		}
	}
}

到这里,AC自动机的核心,fail指针部分就说完了,之后再进行匹配就好了.

下面附上上面给出的那道题的AC代码,不理解的可以再看一下。

#include<bits/stdc++.h>
using namespace std;

int n,m,k[1000005][27],ans,cnt,mark[1000005],fail[1000006];
string a[1000005],s;
void add(string s)//build trie tree
{
	int u=0,l=s.size();
	for(int i=0;i<l;i++)
	{
		int x=s[i]-'a';
		if(k[u][x]==0)	k[u][x]=++cnt;
		u=k[u][x];
	}
	mark[u]++;
}
void getfail()//figure out the fail
{
	queue <int> q;
	for(int i=0;i<=25;i++)
	{
		if(k[0][i])	q.push(k[0][i]);//fail[ch[0][i]]=0;//BFs in
	}
	while(!q.empty())//BFS
	{
		int u=q.front();
		q.pop();
		for(int i=0;i<=25;i++)
		{
			int v=k[u][i];//lazy
			if(v)
			{
				fail[v]=k[fail[u]][i];//jump to fail例子:a调到它爹的fail的a 
				q.push(v);	//add
			}
			else
			{
				k[u][i]=k[fail[u]][i];//压缩路径 
			}
		}
	}
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)	cin>>a[i],add(a[i]);
	cin>>s;
	int ls=s.size();
	int u=0,c;
	getfail();
	for(int i=0;i<ls;i++)
	{
		c=s[i]-'a';
		u=k[u][c];
		for(int p=u;p&&mark[p]!=-1;p=fail[p])
		{
			ans+=mark[p];
			mark[p]=-1;
		}
	}
	cout<<ans<<endl;
	return 0;
}

PS:AC自动机是一个值得欣赏的算法,而自动AC机是违法的(

如有错误求大佬指出,感激不尽

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值