题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2222
思路分析:该问题为多模式匹配问题,使用AC自动机解决;需要注意的问题是如何统计该待查询的字符串包含的关键字:
假设待查找的字符串为str[0..n],则str[i…j]可能为某一个关键字;假设当前正在匹配字符str[k],则以str[i..k]为关键字的所有可能
可能的关键字的最后一个字符为str[k],使用fail指针进行跳转并判断以str[k]结尾的该结点是否为关键字最后一个结点,重复进行该
操作直到回溯到根节点。
代码如下:
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int KIND = 26; const int LEN_STR = 50 + 10; const int MAX_N = 1000000 + 10; struct Node; Node *q[MAX_N]; char insert_str[LEN_STR]; char str[MAX_N]; struct Node { Node *fail; Node *next[KIND]; int count; Node() { fail = NULL; count = 0; memset(next, NULL, sizeof(next)); } }; void Insert(char *str, Node *root) { Node *p = root; int i = 0, index = 0; while (str[i]) { index = str[i++] - 'a'; if (p->next[index] == NULL) p->next[index] = new Node(); p = p->next[index]; } p->count++; // 单词的末尾会被标记为1 } void BuildAcAutomation(Node *root) { int head = 0, tail = 0; root->fail = NULL; q[tail++] = root; while (head != tail) { Node *temp = q[head++]; Node *p = NULL; for (int i = 0; i < KIND; ++i) { if (temp->next[i]) { if (temp == root) temp->next[i]->fail = root; else { p = temp->fail; while (p != NULL) { if(p->next[i]) { temp->next[i]->fail = p->next[i]; break; } p = p->fail; } if (p == NULL) temp->next[i]->fail = root; } q[tail++] = temp->next[i]; } } } } int Query(char *str, Node *root) { int i = 0, cnt = 0, index = 0; Node *p = root; while (str[i]) { index = str[i] - 'a'; while (!p->next[index] && p != root) p = p->fail; p = p->next[index]; p = (p == NULL) ? root : p; Node *temp = p; while (temp != root && temp->count != -1) { cnt += temp->count; temp->count = -1; temp = temp->fail; } ++i; } return cnt; } int main() { int case_times = 0; int ans = 0, n = 0; scanf("%d", &case_times); while (case_times--) { Node *root = new Node(); scanf("%d", &n); while (n--) { scanf("%s", insert_str); Insert(insert_str, root); } BuildAcAutomation(root); scanf("%s", &str); ans = Query(str, root); printf("%d ", ans); } return 0; }