AC自动机

简介

AC自动机,全称Aho-Corasick自动机,适用于存在多个模板串的字符串匹配问题,如果没有AC自动机,你可能需要对n个模板串分别求一趟KMP,但是复杂度过高,而AC自动机可以一次匹配,效率更优秀。

实现

KMP是在字符串上线性匹配,而AC自动机则在字符串的集合上匹配,什么东西可以把一大堆字符串吧、放一起存储?Trie!所以AC自动机其实就是在Trie上生成KMP的失配函数。

对于一棵建好的Trie,接下来就是类似于KMP的失配函数的构造,这里要一层一层的构造,所以需要用BFS来遍历。其他大致相同,这里还有一点,当找到了某个单词后,可能找到其他的单词。例如找到his后有is,所以还要构造一个lst数组表示如果按照失配函数继续走时最近的一个单词编号,用于提高效率(蓝书上提到这个lst数组在正规文献内叫做后缀链接)。

模板

HDU2222

求模板串出现了几个(不能算重)

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=500005;
int n,len,tst,ch[maxn][26],w[maxn],lst[maxn],nxt[maxn],que[maxn];
char s[1000005];
struct AC{
	void clear(int x){memset(ch[x],0,sizeof(ch[x])); w[x]=lst[x]=nxt[x]=0;}
	void insist(){
		int now=0,L=strlen(s);
		for (int i=0;i<L;i++){
			if (!ch[now][s[i]-'a']) {ch[now][s[i]-'a']=++len; clear(len);}
			now=ch[now][s[i]-'a'];
		}
		w[now]++;
	}
	void makeF(){
		int hed=0,til=0;
		for (int i=0;i<26;i++)
			if (ch[0][i]){
				que[++til]=ch[0][i];
				nxt[ch[0][i]]=lst[ch[0][i]]=0;
			}
		while (hed!=til){
			int x=que[++hed];
			for (int i=0;i<26;i++)
				if (ch[x][i]){
					int now=ch[x][i]; nxt[now]=ch[nxt[x]][i];
					lst[now]=(w[nxt[now]])?nxt[now]:lst[nxt[now]];
					que[++til]=now;
				}else ch[x][i]=ch[nxt[x]][i]; //记忆化操作,提高效率
		}
	}
	int get_num(int x){
		int num=0;
		for (;x;w[x]=0,x=lst[x]) num+=w[x]; //如果是求匹配个数就把w[x]=0去掉
		return num;
	}
	int find(){
		int now=0,num=0,L=strlen(s);
		for (int i=0;i<L;i++){
			now=ch[now][s[i]-'a'];
			num+=get_num(now);
		}
		return num;
	}
}tr;
void _work(){
	scanf("%d",&n); len=0; tr.clear(0);
	for (int i=1;i<=n;i++){scanf("%s",s); tr.insist();}
	tr.makeF(); scanf("%s",s); printf("%d\n",tr.find());
}
int main()
{
	freopen("search.in","r",stdin);
	freopen("search.out","w",stdout);
	scanf("%d",&tst);
	while (tst--) _work();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值