AC自动机

1.参考资料:

  http://blog.csdn.net/mobius_strip/article/details/22549517

  http://www.cppblog.com/mythit/archive/2009/04/21/80633.html

   http://blog.csdn.net/niushuai666/article/details/7002823

http://hzwer.com/2170.html

2.什么是AC自动机?

  AC自动机并不意味这你学会了就能自动AC了......

  AC自动机就是在trie树上写kmp.....

  那么怎么写呢?尤其是怎么确定next数组指向的值呢?

如图(图是网上找的)

我们构造一棵trie树,然后对于每一个点,顺着其父亲节点的next指针往上走,直到找到一个节点,它有一个子节点和该节点的字符相同(其实应该字符在边上,不过放到点上也没关系啦),那么该节点的next指针就指向这个子节点。

在我们匹配字符串的时候,next指针可以快速帮助匹配,方式和kmp很像,寻找下一个字符路径的时候也是这么顺着next指针往上,然后找子节点。

这么说可能还有点迷糊,看看代码就明白了。

我之前写AC自动机总是要130行+,但是自从看了hzwer的AC自动机,我觉得整个世界都美好起来了。

3.代码(其实我是学的hzwer的)

题目是HDU2222,模板题。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iomanip>
#include<cmath>
#include<algorithm>
using namespace std;
int T,sz,ans,n;
char s[51],ss[1000001];
int a[500001][27],q[500001],fa[500001],danger[500001];
bool mak[500001];
void tri(){//构建trie树
	int from=1,i,j,sum,l=strlen(s);
	for(i=0;i<l;i++){
		sum=s[i]-'a'+1;
		if(a[from][sum]){from=a[from][sum];}
		else {
			sz++;a[from][sum]=sz;from=sz;fa[sz]=mak[sz]=danger[sz]=0;
			for(j=1;j<=26;j++)a[sz][j]=0;
		}
	}
	danger[from]++;
}
void ac(){//寻找失配指针next
	int from,i,j,head=1,tail=1;
	q[1]=1;fa[1]=0;
	while(head<=tail){
		from=q[head];
		for(i=1;i<=26;i++){
			if(!a[from][i])continue;
			j=fa[from];
			while(!a[j][i])j=fa[j];
			fa[a[from][i]]=a[j][i];//根节点从1开始,那么0是摆看的,很巧妙的处理。
			tail++;q[tail]=a[from][i];
		}
		head++;
	}
}
void solve(){
	int i,j,from=1,sum,l=strlen(ss);
	for(i=0;i<l;i++){
		mak[from]=1;
		sum=ss[i]-'a'+1;
		while(!a[from][sum])from=fa[from];
		from=a[from][sum];
		if(!mak[from])//顺着失配边网上寻找危险节点
			for(j=from;j;j=fa[j]){
				ans+=danger[j];danger[j]=0;
			}
	}
	printf("%d\n",ans);
}
int main()
{
   	int i,j;
   	scanf("%d",&T);
   	while(T--){
   		sz=1;ans=0;mak[1]=fa[1]=danger[1]=0;
   		scanf("%d",&n);
   		for(i=1;i<=26;i++)a[0][i]=1,a[1][i]=0;
   		for(i=1;i<=n;i++){
   			scanf("%s",s);tri();
   		}
   		ac();
   		scanf("%s",ss);
   		solve();
   	}
   	return 0;
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值