AC自动机

\(前置cheese\)

一、简介:

\(KMP\)类似,\(AC\)自动机也是用来处理字符串匹配的问题。与\(KMP\)不同的是,\(KMP\)用来处理单模式串问题,即问模式串\(T\)是否是其他主串\(S_i\)的子串,而\(AC\)自动机则能处理多模式串的问题.\(AC\)自动机处理的常见问题如:给出\(n\)个单词\(T_i\),再给出一段包含\(m\)个字符的文章\(S\),问有多少个单词在文章里出现了。

构建一个\(AC\)自动机并用于匹配需要三个步骤:将所有的模式串构建成一棵\(Trie\),对\(Trie\)上所有的节点构造失败指针\((fail)\),利用失败指针对主串进行模式匹配。实际上这个失败指针与\(KMP\)算法中的\(next\)数组非常相似,因而\(AC\)自动机可以看做是\(Trie\)\(KMP\)算法的结合(\(Trie\)上的\(KMP\)算法)。

我们将模式串所构出的\(Trie\)的节点称为\(AC\)自动机的节点(状态),\(Trie\)中的边称为\(AC\)自动机中的边(转移),将失配指针所对应的节点称为失配转移。

二、算法流程:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,cnt=0;
char tmp[1000100];
struct node
{
	int fail,num;
	int ch[30];
}tr[1000100];
int q[1000100];
void build()
{
	int len=strlen(tmp),u=0;
	for(int i=0;i<len;i++)
	{
		int s=tmp[i]-'a';
		if(tr[u].ch[s]==0) tr[u].ch[s]=++cnt;
		u=tr[u].ch[s];
	}
	tr[u].num++;
}
void get_fail()
{
	int l=0,r=0;
	for(int i=0;i<26;i++)
		if(tr[0].ch[i]!=0)
		{
			tr[tr[0].ch[i]].fail=0;
			q[++r]=tr[0].ch[i];
		}
	while(l<r)
	{
		int u=q[++l];
		for(int i=0;i<26;i++)
		{
			int v=tr[u].ch[i];
			if(v)
			{
				tr[v].fail=tr[tr[u].fail].ch[i];//处理fail指针,是它父亲的fail指针的i儿子
				q[++r]=v;
			}
			else tr[u].ch[i]=tr[tr[u].fail].ch[i];//处理所说的“虚指针”,WA了好几次因为把这里的tr[u].ch[i]也替换成v了。。
		}
	}
}
int AC()
{
	int len=strlen(tmp);
	int u=0,ans=0;
	for(int i=0;i<len;i++)
	{
		int s=tmp[i]-'a';
		u=tr[u].ch[s];
		int v=u;
		while(v && tr[v].num!=-1)
		{
			ans+=tr[v].num;
			tr[v].num=-1;//加过了之后就不用再加了
			v=tr[v].fail;
		}
	}
	return ans;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",tmp);
		build();
	}
	tr[0].fail=0;
	get_fail();
	scanf("%s",tmp);
	printf("%d",AC());
	return 0;
}

posted on 2020-09-29 19:02  fishsit  阅读(94)  评论(0)    收藏  举报

导航