HDU 2222 (AC自动机模板)

来源:互联网 发布:c语言flag怎么用 编辑:程序博客网 时间:2024/05/29 19:02

Keywords Search

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 10744    Accepted Submission(s): 3722


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


思路:AC自动机模板,具体步骤有:1.根据关键字构造字典树,每个节点都由一个接收态变量,失败指针, 指向子节点的指针构成。2.对字典树的节点构造失败指针,用BFS按层次构造失败指针,每一个节点的失败指针都沿着父节点的失败指针去找,直到找到一个祖先节点含与当前字符相同的字符,将失败指针指向该字符的节点。3.将文章进行匹配,每次匹配成功都要考虑该节点是否为接收态,当前结点失败指针指向的是否为接收态。

代码即注释如下:

#include<cstdio>#include<iostream>#include<cstring>#include<cmath>#include<cstdlib>#include<queue>using namespace std;char text[1000005];char str[55];struct Node//字典树的节点 {int end;//接收态,代表有几个关键词以该节点的字符为结尾 Node * fail, *child[26];};Node * initNode()//新建一个节点 {Node * ret = (Node *)malloc(sizeof(Node));ret->end = 0;ret->fail = NULL;memset(ret->child, NULL, sizeof(ret->child));return ret;}void createTree(Node * p,char * s)//创建字典树 {int index = *s - 'a';if(p->child[index] == NULL)//若没有该节点,则创建一个该字符的节点 {p->child[index] = initNode();}if(*(s + 1))//若关键词还有下一位字符,则递推 {createTree(p->child[index], s + 1);}else//如果当前位已经是一个关键词的结尾,则接收态+1 {p->child[index]->end++;}}void createFail(Node * p)//通过广搜创建失败指针 {queue<Node *> q;q.push(p);while(!q.empty()){Node * pos = q.front();q.pop();for(int i = 0; i < 26; i++)//遍历所有子节点 {Node * f = pos->fail;//父节点的失败指针 if(pos->child[i]) {while(f != NULL)//跟着父节点的失败指针走,若不为空 {if(f->child[i])//如果一路走, {//发现某个失败指针指向的节点的子节点有与当前节点相同的 pos->child[i]->fail = f->child[i];//则当前结点指向该找到的节点 break;}else {f = f->fail;//没有找到,继续沿着失败指针走 }}if(f == NULL)//如果一直未找到,最后指向了空 {pos->child[i]->fail = p;//则赋值为根节点,即从根开始 }q.push(pos->child[i]);}}}}int acauto(Node * p, char * text)//匹配 {int ans = 0, len;len = strlen(text);Node * f = p;for(int i = 0; i < len; i++){int index = text[i] - 'a';while(f){if(f->child[index])//若匹配到 {f = f->child[index];//指针向下移动 break;}else//若失配 {f = f->fail;//找失配指针指向的节点 }} if(f == NULL)//指针为空 {f = p;//则从头开始 continue;//下一循环开始 }Node * temp = f;//匹配到一位之后 while(temp != p)//当不为根节点时就继续循环 { if(temp->end)//若接收态不为0,则为一个关键字的结尾 {ans += temp->end;temp->end = 0;//按照本题题意,每个关键词只计数一次 }temp = temp->fail;//该字符的失败指针指向的肯定是与该字符相同的,有可能是接收态 }}return ans;}int main(){int i, j, cases, n;scanf("%d", &cases);while(cases--){Node * root = initNode();scanf("%d", &n);for(i = 0; i < n; i++){scanf("%s", str);createTree(root, str);}scanf("%s",text);createFail(root);printf("%d\n",acauto(root,text));}return 0;} 

0 0
原创粉丝点击