HDU 2222 ACAM模板(AC自动机)

这里找到了两篇很nice的Trie树(作者Hackbuteer1)以及AC自动机(作者niushuai666)入门详解。博主写的可以说是非常用心了,一看就懂。

题意:给出N(<=10000)个单词(长度<=50)组成的字典。求串T(长度<=1e6)中出现了字典中的多少个单词。

题解:AC自动机裸题。所谓AC自动机就是Trie树加上了KMP的fail指针,而且是多模式的fail指针,意思就是说Trie图是有相同前缀的两个单词会有相同的一段root出发的路径,这个fail指针不再是KMP里某一个串的后缀和前缀相同并取最大,而是当前这个点得到的串的后缀和所有串中某个前缀相同且取最大。求这个fail就只要把KMP的过程改成广搜就行了,因为每个字符下一个状态可以是若干字符。。。每次把T中下一个字符考虑进来,通过fail指针转移到Trie图上的一个点上,然后统计root到这个点得到的串 的所有后缀的cnt(就是通过fail一点一点跳到根,把路径上的答案都统计起来。并把统计过的答案置为-1防止重复计数,同时fail跳到答案是-1的点表示这个点到根的答案已经统计过了,那么就提前停下来。)。

模板是我自己写的。。。可能比较丑。。。马上我会找一个好看的贴过来233

Code:

#include<bits/stdc++.h>
using namespace std;
const int MAX1 = 55;
const int MAX2 = 1000050;
char a[MAX1];
char b[MAX2];
struct ACAM {
	int id;
	int cnt;
	ACAM* fail;
	ACAM* nxt[26];
};
ACAM* root;
void ACAM_Clear(ACAM* root){
	for (int i=0;i<=25;i++){
		if (root->nxt[i]!=NULL){
			ACAM_Clear(root->nxt[i]);
		}
	}
	free(root);
};
ACAM* ACAM_Create(int id){
	ACAM* node = (ACAM*)(malloc(sizeof(ACAM)));
	node->cnt=0;
	node->fail = NULL;
	node->id =id;
	memset(node->nxt,0,sizeof(node->nxt));
	return node;
}
void ACAM_Insert(ACAM* root,char* word){
	ACAM* node = root;
	char* p = word;
	while (*p){
		int id = *p-'a';
		if (node->nxt[id]==NULL){
			node->nxt[id] = ACAM_Create(id);
		}
		node = node->nxt[id];
		p++; 
	}
	node->cnt++;
}
ACAM* que[MAX2*10];
void ACAM_Build(ACAM* root){
	int l=0,r=0;
	for (int i=0;i<=25;i++){
		if (root->nxt[i]!=NULL){
			root->nxt[i]->fail = root;
			for (int j=0;j<=25;j++){
				if (root->nxt[i]->nxt[j]!=NULL){
					root->nxt[i]->nxt[j]->fail = root;
					r++;
					que[r] = root->nxt[i]->nxt[j];
				}
			}
		}
	}
	while (l<r){
		l++;
		ACAM* q = que[l];
//		printf("%d %c\n",l,q->id+'a');
		while (q->fail!=root&&q->fail->nxt[q->id]==NULL){
			q->fail = q->fail->fail;
		}
		if (q->fail->nxt[q->id]!=NULL){
			q->fail = q->fail->nxt[q->id];
		}
//		printf("%d fial = %c\n",l,q->fail->id+'a');
		for (int i=0;i<=25;i++){
			if (q->nxt[i]!=NULL){
				q->nxt[i]->fail = q->fail;
				r++;
				que[r] = q->nxt[i];
			}
		}
	}
}
int ACAM_Search(ACAM* root,char*word){
	int ans=0;
	ACAM* node = root;
	char* p = word;
	while (*p){
//		printf("%c\n",*p);
		int id = *p-'a';
		while (node->nxt[id]==NULL&&node!=root){
			node=node->fail;
		}
		if (node->nxt[id]!=NULL){
			node =  node->nxt[id];
		}
		ACAM* temp = node;
		while (temp!=root&&temp->cnt!=-1){
			ans+=temp->cnt;
			temp->cnt = -1;
			temp=temp->fail;
		}
		p++;
	}
	return ans;
}
void init(){
	ACAM_Clear(root);
	root = ACAM_Create(-1);
}
void input(){
	int n;
	scanf("%d",&n);
	while (n--){
		scanf("%s",a);
		ACAM_Insert(root,a);
	}
}
void solve(){
	ACAM_Build(root);
	scanf("%s",b);
	printf("%d\n",ACAM_Search(root,b));
}
int main(){
	int t;
	scanf("%d",&t);
	root = ACAM_Create(-1);
	while (t--){
		init();
		input();
		solve();
	}
	return 0;
} 


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值