HDU2222【AC自動機】
阿新 • • 發佈:2019-02-08
#include<algorithm> #include<iostream> #include<queue> using namespace std; const int maxn=1000000+5;//代表樹的大小 const int maxm=26;//代表孩子分支 char a[maxn]; struct Trie { int trieN; int ch[maxn][maxm]; int val[maxn]; //標記已經匹配過的點 int fail[maxn]; //求出根節點到這個節點與根節點到某個點相同的最長前後綴。讓它指向最長字首的後一個節點。 void init() { trieN = -1; newnode(); } int newnode() //第一層節點fail都指向root=0,而新建一個節點的時候fail被清空為0(所以所有節點的fail都預設為0) { memset(ch[++trieN], 0, sizeof(ch[0])); val[trieN] = fail[trieN] = 0; return trieN; } void insert(const char *str) // { int cur = 0; for (int i = 0;str[i];i++) { int d = str[i]-'a'; //d = str[i] - 'a'; if (!ch[cur][d]) ch[cur][d] = newnode(); //在節點cur要新建一個孩子節點child cur = ch[cur][d]; //讓cur指向這個孩子節點。 } val[cur]++; //這個節點作為這個單詞最後一個字元,標記一下。 } void build() //求fail陣列。 { queue<int> q; for (int i = 0;i < maxm;i++) { if (ch[0][i]) //讓第一層節點的fail都指向root再放進佇列裡 q.push(ch[0][i]); } while (!q.empty()) { int cur = q.front(); q.pop(); //在這裡 0=‘a’,1=‘b’.... for (int i = 0;i < maxm;i++) //假設取的是0層的‘a’,對它的分支做處理。 { if (ch[cur][i]) { //如果當前節點cur的孩子節點child存在,那麼ch[cur][child]的fail應指向最長相同前後綴的字首的最後一個節點,相當於建立fail fail[ch[cur][i]] = ch[fail[cur]][i]; q.push(ch[cur][i]); } else//如果當前節點cur的孩子節點child不存在,ch[cur][child]代替fail指標直接指向最長相同前後綴的字首的最後一個節點相應的孩子節點child ch[cur][i] = ch[fail[cur]][i]; } } } int query(const char *str) { int res = 0, cur = 0; //從頭開始找。 for (int i = 0;str[i];i++) { int d = str[i]-'a'; //d = str[i] - 'a'; cur = ch[cur][d]; int tmp = cur; while (tmp) { if (val[tmp]==-1) break; res += val[tmp]; val[tmp]=0; val[tmp] = -1; //已經匹配過的要標記一下,往回遍歷時val值為-1的,就不再繼續了。 tmp = fail[tmp]; } } return res; } }ac; char s[50+5]; int main() { int t; scanf("%d",&t); while(t--) { int n; scanf("%d",&n); getchar(); ac.init(); for(int i=1;i<=n;i++) { scanf("%s",s); ac.insert(s); } ac.build(); scanf("%s",a); printf("%d\n",ac.query(a)); } return 0; }