hdoj 2222 Keywords Search 【字典树挤进1ms----啊--我要学AC自动机】


传送门:2222


正要学AC自动机--发现  k*n=5*10^7  --  就用单纯的字典树过了--

建树--扫描字符串--统计---


代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct trie{
	trie * child[26];
	int shu;
	int hao;
}P[600000];
int ii=0,kp,op[10100];
trie * boot = & P[0];
void create(char xx[],int ii)//建树
{
	trie * k=boot;
	int j,ll=strlen(xx);
	for (int i=0;i<ll;i++)
	{
		j=xx[i]-'a';
		if (!k->child[j])
		{
			P[kp].hao=kp;
			P[kp].shu=0;
			k->child[j]=&P[kp++];
		}
		k=k->child[j];
	}
	op[ii]=k->hao;
}
void sou(char xx[])//匹配
{
	trie * k=boot;
	int j;
	for (int i=0;xx[i];i++)
	{
		j=xx[i]-'a';
		if (!k->child[j])
		return;
		k=k->child[j];
		k->shu++;
	}
}
void sol()
{
	int n;scanf("%d",&n);
	char ch[60];kp=1;
	memset(P,0,sizeof(P));
	for (int i=1;i<=n;i++)
	{
		scanf("%s",ch);
		create(ch,i);
	}
	char opop[1000100];
	scanf("%s",opop);
	int ll=strlen(opop);
	for (int i=0;i<ll;i++)
	sou(opop+i);
	int s=0;
	for (int i=1;i<=n;i++)//统计
	if (P[op[i]].shu)
	s++;
	printf("%d\n",s);
}
int main()
{
	int t;scanf("%d",&t);
	while (t--) sol();
	return 0;
}

—————————————————————————————————————————————————————————————————————————————

AC机中主要的是fail指针的创建,这里采用bfs来一层一层的创建,每个结点的fail所指的层数当然小于它自身的层数呀,

然后第一层的都指向root,

剩下的举个例子

ABCDE   加入D的fail指向了  CD的D,那么我们求E的fail时,就是找一个D的fail  或  fail...的fail..... ,第一个后面有E的,那个E就是 ABCDE的E的fail所指的。

AC机代码:

#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
using namespace std;
long long ans ;
char ch[60];
char str[10001000];
struct node{
	node * next[26];
	node * fail;
	int sum;
	bool falg;
	node():sum(0),falg(false),fail(NULL){
		memset(next,0LL,sizeof(next));
	}
};
node * root;
void build(){
	int ll = strlen(ch),x;
	node * p = root;
	for (int i=0;i<ll;i++){
		x = ch[i]-'a';
		if (p->next[x]==NULL)	p->next[x] = new node();
		p = p->next[x];
	}
	p->sum++;
//	printf("%s  %d  %d\n",ch,p->sum,p);
}

void dele(node * root){
	for (int i=0;i<26;i++){
		if (root->next[i]!=NULL){
			dele(root->next[i]);
		}
	}
	delete root;
}

void getfail(){
	root->fail = root;
	queue<node *> que;
	que.push(root);
	int ca = 2;
	while (!que.empty()){
		node * temp = que.front();
		que.pop();
		for (int i=0;i<26;i++){
			if (temp->next[i]!=NULL){
				node * p = temp;
				que.push(p->next[i]);
				while (p!=root){
					
					if (p->fail->next[i]!=NULL){
						temp->next[i]->fail = p->fail->next[i];
						break;
					}else{
						p = p->fail;
					}
					
				}
				if (p==root){
					temp->next[i]->fail = root;
				}
			//	temp->next[i]->sum += (temp->next[i]->fail->sum);
			}
		}
	}
}
void sumToZero(node * p){
	while (!p->falg){
		ans += p->sum;
		
		p->falg = true;
		
		p = p->fail;
	}
}
void AC(){
	node * p = root;
	
	int ll = strlen(str),x;
	
	for (int i=0;i<ll;i++){
		
		x = str[i] - 'a';
		if (p->next[x]!=NULL){
			p = p->next[x];
		}else{
			while (p!=root){
				p = p->fail;
				if (p->next[x]!=NULL){
					p=p->next[x];
					break;
				}
			}
		}
//		if (p->sum!=0){
//			ans += p->sum;
			sumToZero(p);
//			printf("%c   %d  ---\n",str[i],ans);
//		}		
	}
}

void slove(){
	int n;scanf("%d",&n);
	root = new node();
	root->falg = true;
	for (int i=0;i<n;i++){
		scanf("%s",ch);
		build();		
	}
	
	getfail();
	
	scanf("%s",str);
	ans = 0;		
	AC();
	printf("%lld\n",ans);
	dele(root);
}
int main()
{
	int T;scanf("%d",&T);
	while (T--){
		slove();
	}
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值