HDU 2222 Keywords Search

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2222

题意:给定一个字符串和n个单词,问字符串中共出现了多少个单词。

解析:这是AC自动机最基础的题目,也是我接触的第一个AC自动机的题目。AC自动机的话,大家可以去参考一下http://blog.csdn.net/niushuai666/article/details/7002823这篇博客,我感觉讲的很详细。然后基本上大致编出来AC自动机没问题。

但是有些需要注意的地方

1.在储存节点是否为单词节点时(我是用val,上面的链接中用的是count)一定要注意出现的单词可能重复,所以必然val是++,

2.不能直接memset(ch,0,sizeof(ch)),这样的话会MLE,虽说不知道为什么╮(╯▽╰)╭

 

#include"stdio.h"

#include"string.h"

#include"queue"

using namespace std;

#define maxnode (500005)

#define sigma_size 26



struct AC_automation{			

	struct node{

		int next[sigma_size];

		int val;	//树节点的附加信息

		int fail;	//存储节点的失配指针

		void init(){

			memset(next,0,sizeof(next));

			fail = val = 0;

		}

	}ch[maxnode];	//用来存储Trie树

	int sz;							//整棵树节点的总数 

	int idx(char c){

		return c - 'a';		//对于小写字母集,获得c的编号

	}



	void init(){

		sz = 1;

		ch[0].init();

	//	memset(ch,0,sizeof(ch)); //虽然不知道为什么,但是加上这句就MLE

	}

	void insert(char *s){

		int u = 0,len = strlen(s);

		for(int i = 0 ; i < len ; i ++){

			int c = idx(s[i]);

			if(ch[u].next[c] == 0){		

				ch[sz].init();

				ch[u].next[c] = sz++;	

			}

			u = ch[u].next[c];

		}

		ch[u].val++;		//此处不可以是ch[u].val = 1;因为可能有多个相同的单词

	}



	//利用bfs得到失配指针fail

	void get_fail(){

		queue<int>Q;

		for(int i = 0 ; i < sigma_size; i ++)

			if(ch[0].next[i] != 0)

				Q.push(ch[0].next[i]);

		while(!Q.empty()){

			int temp = Q.front();

			Q.pop();

			for(int i = 0 ; i < sigma_size; i ++){

				if(ch[temp].next[i] != 0){

					Q.push(ch[temp].next[i]);

					int fail_temp = ch[temp].fail;

					//关键步骤,如果fail指针所指的节点没有('a'+i)这个子节点,继续递归,直到fail_temp指向0节点,即根节点

					while(fail_temp > 0 && ch[fail_temp].next[i] == 0) 

						fail_temp = ch[fail_temp].fail;



					if(ch[fail_temp].next[i] != 0)	//如果fail_temp节点的子节点有('a'+i)这个字符

							fail_temp = ch[fail_temp].next[i];

					ch[ch[temp].next[i]].fail = fail_temp;   //子节点的失配节点由父节点节点决定

				}

			}

		}

	}



	//寻找text[]总共有多少个单词

	int get_word_num(char *text){

		int ans = 0;

		int len = strlen(text);

		int node = 0,fail_temp = 0;

		for(int i = 0 ; i < len ; i ++){

			int character = idx(text[i]);

			while(fail_temp > 0 && ch[fail_temp].next[character] == 0)	

				//若没有text[i]这个字符,则使用失配指针继续在Trie树上遍历

					fail_temp = ch[fail_temp].fail;



			if(ch[fail_temp].next[character] != 0){	//此时找到了text[i]这个字符

				fail_temp = ch[fail_temp].next[character];

				int fail_temp2 = fail_temp;			//此处必须有另一个变量来记录失配的时候的下标

				while(fail_temp2 > 0 && ch[fail_temp2].val){   //在向上遍历失配函数的时候,只有当该节点是单词节点,才会继续失配

					ans += ch[fail_temp2].val;

					ch[fail_temp2].val = 0;					//防止重复计算,把该单词节点删去

					fail_temp2 = ch[fail_temp2].fail;	

				}

			}

		}

		return ans;

	}

}AC;

int N;

char text[1000010];



int main(){

	int T;

	scanf("%d",&T);

	while(T--){

		scanf("%d",&N);

		char str_temp[55];

		AC.init();

		for(int i= 0 ; i < N; i ++){

			scanf("%s",str_temp);

			AC.insert(str_temp);

			memset(str_temp,0,sizeof(str_temp));

		}

		AC.get_fail();

		scanf("%s",text);

		printf("%d\n",AC.get_word_num(text));

	}

	return 0;

}


 

 

你可能感兴趣的:(search)