1. 程式人生 > >HDU2222(AC自動機模板題)

HDU2222(AC自動機模板題)

咳咳~ 因為暫時看不懂,所以先儲存個模板題吧。

Keywords Search Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 50101 Accepted Submission(s): 16083

Problem Description In the modern time, Search engine came into the life of everybody like Google, Baidu, etc. Wiskey also wants to bring this feature to his image retrieval system. Every image have a long description, when users type some keywords to find the image, the system will match the keywords with description of image and show the image which the most keywords be matched. To simplify the problem, giving you a description of image, and some keywords, you should tell me how many keywords will be match.

Input First line will contain one integer means how many cases will follow by. Each case will contain two integers N means the number of keywords and N keywords follow. (N <= 10000) Each keyword will only contains characters ‘a’-‘z’, and the length will be not longer than 50. The last line is the description, and the length will be not longer than 1000000.

Output Print how many keywords are contained in the description.

Sample Input 1 5 she he say shr her yasherhs

Sample Output 3 題意:第一行輸入測試資料的組數,然後輸入一個整數n,接下來的n行每行輸入一個單詞,最後輸入一個字串,問在這個字串中有多少個單詞出現過。 解題思路:這是一道多模式串的字元匹配問題,又名AC自動機,聽著名字好高大上的趕腳。KMP處理的是單模式串匹配,其實KMP也能處理多模式串匹配的問題,不過當資料很大的時候,是相當的耗費時間,所以就有前輩們發明了AC自動機這個高效的多模式串匹配演算法。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Node
{
	int cnt;//是否為該單詞的最後一個結點 
	Node *fail;//失敗指標 
	Node *next[26];//Trie中每個結點的各個節點 
}*queue[500005];//佇列,方便用BFS構造失敗指標 
char s[1000005];//主字串 
char keyword[55];//需要查詢的單詞 
Node *root;//頭結點 
void Init(Node *root)//每個結點的初始化 
{
	root->cnt=0;
	root->fail=NULL;
	for(int i=0;i<26;i++)
		root->next[i]=NULL;
}
void Build_trie(char *keyword)//構建Trie樹 
{
	Node *p,*q;
	int i,v;
	int len=strlen(keyword);
	for(i=0,p=root;i<len;i++)
	{
		v=keyword[i]-'a';
		if(p->next[v]==NULL)
		{
			q=(struct Node *)malloc(sizeof(Node));
			Init(q);
			p->next[v]=q;//結點連結 
		}
		p=p->next[v];//指標移動到下一個結點 
	}
	p->cnt++;//單詞最後一個結點cnt++,代表一個單詞 
}
void Build_AC_automation(Node *root)
{
	int head=0,tail=0;//佇列頭、尾指標 
	queue[head++]=root;//先將root入隊 
	while(head!=tail)
	{
		Node *p=NULL;
		Node *temp=queue[tail++];//彈出隊頭結點 
		for(int i=0;i<26;i++)
		{
			if(temp->next[i]!=NULL)//找到實際存在的字元結點 
			{ //temp->next[i] 為該結點,temp為其父結點 
				if(temp==root)//若是第一層中的字元結點,則把該結點的失敗指標指向root 
					temp->next[i]->fail=root;
				else
				{
					//依次回溯該節點的父節點的失敗指標直到某節點的next[i]與該節點相同,
                	//則把該節點的失敗指標指向該next[i]節點; 
                	//若回溯到 root 都沒有找到,則該節點的失敗指標指向 root
					p=temp->fail;//將該結點的父結點的失敗指標給p 
					while(p!=NULL)
					{
						if(p->next[i]!=NULL)
						{
							temp->next[i]->fail=p->next[i];
							break;
						}
						p=p->fail;
					}
					//讓該結點的失敗指標也指向root 
					if(p==NULL)
						temp->next[i]->fail=root;
				}
				queue[head++]=temp->next[i];//每處理一個結點,都讓該結點的所有孩子依次入隊 
			}
		}
	}
}
int query(Node *root)
{ //i為主串指標,p為模式串指標 
	int i,v,count=0;
	Node *p=root;
	int len=strlen(s);
	for(i=0;i<len;i++)
	{
		v=s[i]-'a';
		//由失敗指標回溯查詢,判斷s[i]是否存在於Trie樹中 
		while(p->next[v]==NULL && p!=root)
			p=p->fail;
		p=p->next[v];//找到後p指標指向該結點 
		if(p==NULL)//若指標返回為空,則沒有找到與之匹配的字元 
			p=root;
		Node *temp=p;//匹配該結點後,沿其失敗指標回溯,判斷其它結點是否匹配 
		while(temp!=root)//匹配結束控制 
		{
			if(temp->cnt>=0)//判斷該結點是否被訪問 
			{
				count+=temp->cnt;//由於cnt初始化為 0,所以只有cnt>0時才統計了單詞的個數 
				temp->cnt=-1;//標記已訪問過 
			}
			else//結點已訪問,退出迴圈 
				break;
			temp=temp->fail;//回溯 失敗指標 繼續尋找下一個滿足條件的結點 
		}
	}
	return count;
}
int main()
{
	int T,n;
	scanf("%d",&T);
	while(T--)
	{
		root=(struct Node *)malloc(sizeof(Node));
		Init(root);
		scanf("%d",&n);
		for(int i=0;i<n;i++)
		{
			scanf("\n%s",keyword);
			Build_trie(keyword);
		}
		Build_AC_automation(root);
		scanf("\n%s",s);
		printf("%d\n",query(root));
	}
	return 0;
}