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

hdu 2222 AC自動機(模板題)

模式串 har sizeof 問題 null word 指向 pan 子節點

<題目鏈接>

題目大意:

給你一些單詞,和一個字符串,問你這個字符串中含有多少個上面的單詞。

解題分析:

這是多模匹配問題,如果用KMP的話,對每一個單詞,都跑一遍KMP,那麽當單詞數量非常多的時候,耗時會非常多,所以這裏用到了AC自動機,這是一種類似於Trie樹的數據結構,但是同時,它也用到了KMP算法中 next數組的思想。

下面是AC自動機指針形式的題解:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Node
{
    int cnt;//是否為該單詞的最後一個結點 
Node *fail;//失敗指針 Node *next[26];//Trie中每個結點的各個節點 void Init() { cnt = 0; fail = NULL; for (int i = 0; i<26; i++) next[i] = NULL; } }*queue[500005];//隊列,方便用BFS構造失敗指針 char s[1000005];//主字符串 char keyword[55];//需要查找的單詞 Node *root;//頭結點 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)); q->Init(); p->next[v] = q;//
結點鏈接 } p = p->next[v];//指針移動到下一個結點 } p->cnt++;//單詞最後一個結點cnt++,代表一個單詞 } void Build_AC_automation(Node *root) //利用bfs,建立每個節點的fail指針 { int head = 0, tail = 0;//隊列頭、尾指針 queue[tail++] = root;//先將root入隊 while (head < tail) { Node *p = NULL; Node *temp = queue[head++];//彈出隊頭結點 for (int i = 0; i<26; i++) //接下來,為temp的每一個兒子節點找相對應的fail指針 { 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[tail++] = 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)); root->Init(); 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; }

2018-08-05

hdu 2222 AC自動機(模板題)