1. 程式人生 > >Trie圖(模板)

Trie圖(模板)

truct 指向 判斷 inf fail strlen 分享 scan print

Trie圖(蒟蒻聽說AC自動機能做的題Trie圖都能做,而且AC自動機可能被卡,就沒學過AC自動機),最近想撿一撿,好久之前做的了。

Trie圖,就是一個在Trie樹上建的圖 大概描述一下

比如說有幾個字符串:

abc

abcd

bcd

bacd

jdr

ac

先把它們存在Trie樹中:

技術分享圖片

就像KMP那樣,做出這樣的邏輯判斷:

bacd比較到第三位bac結果沒有d,但起碼bac有了,所以以bac為前綴的或以bac後綴為前綴的串是不用再比較前綴了。

所以出現了fail指針,為失配情況重新定位方案。

類似於next數組。

無解(定位不到失配後新方案),就指向根表示無解。

顯而易見,首字母失配是一定沒有方案的。

其次,一個字母失配可以定位到父節點失配的自己值子節點。

一個優化:沒有新子節點的節點直接指向fail指針自己值子節點。

建出來Trie圖是這樣的:

技術分享圖片

匹配時類似KMP:

模板代碼(luogu P3808 【模板】AC自動機(簡單版)):

 1 #include<queue>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 struct trnt{
 6     int ch[26];
 7     int
val; 8 int fl; 9 }tr[1000000]; 10 std::queue<int>Q; 11 char tmp[1000003]; 12 int siz; 13 int n; 14 void add(char *a) 15 { 16 int len=strlen(a+1); 17 int root=0; 18 for(int i=1;i<=len;i++) 19 { 20 int c=a[i]-a; 21 if(!tr[root].ch[c]) 22 tr[root].ch[c]=++siz;
23 root=tr[root].ch[c]; 24 } 25 tr[root].val++; 26 } 27 void Build() 28 { 29 int root=0; 30 for(int i=0;i<26;i++) 31 if(tr[root].ch[i]) 32 Q.push(tr[root].ch[i]); 33 while(!Q.empty()) 34 { 35 root=Q.front(); 36 Q.pop(); 37 for(int i=0;i<26;i++) 38 { 39 if(tr[root].ch[i]) 40 { 41 tr[tr[root].ch[i]].fl=tr[tr[root].fl].ch[i]; 42 Q.push(tr[root].ch[i]); 43 }else 44 tr[root].ch[i]=tr[tr[root].fl].ch[i]; 45 } 46 } 47 return ; 48 } 49 int Cal(char *a) 50 { 51 int len=strlen(a+1); 52 int ans=0; 53 int root=0; 54 for(int i=1;i<=len;i++) 55 { 56 int c=a[i]-a; 57 root=tr[root].ch[c]; 58 for(int j=root;j&&(tr[j].val!=-1);j=tr[j].fl) 59 { 60 ans+=tr[j].val; 61 tr[j].val=-1; 62 } 63 } 64 return ans; 65 } 66 int main() 67 { 68 scanf("%d",&n); 69 for(int i=1;i<=n;i++) 70 { 71 scanf("%s",tmp+1); 72 add(tmp); 73 } 74 Build(); 75 scanf("%s",tmp+1); 76 printf("%d\n",Cal(tmp)); 77 return 0; 78 }

Trie圖(模板)