AC自动机入门经典题目(两种表达方式)
Keywords Search
指针方式:
1 /* Keywords Search */ 2 # include <iostream> 3 # include <stdio.h> 4 # include <string.h> 5 # include <string> 6 # include <cstdlib> 7 # include <ctime> 8 # include <cmath> 9 # include <cctype> 10 # include <vector> 11 # include <deque> 12 # include <queue> 13 # include <stack> 14 # include <climits> 15 # include <bitset> 16 # include <set> 17 # include <map> 18 using namespace std; 19 20 # define N 1001000 21 # define INF 0x3f3f3f3f 22 # define lowbit(x)(x&(-x)) 23 24 struct node 25 { 26 int cnt; 27 node *next[26]; 28 node *fail; 29 node(){ 30 cnt=0; 31 fail=NULL; 32 memset(next, NULL, sizeof(next)); 33 } 34 }; 35 node *root; 36 char s[55], str[N]; 37 int n; 38 39 void init() 40 { 41 root = new node; 42 } 43 44 void _Insert(char *ss) 45 { 46 int len = strlen(ss); 47 node *p=root; 48 for(int i=0; i<len; i++ ) 49 { 50 int t=ss[i]-'a'; 51 if( p->next[t]==NULL ) 52 p->next[t]=new node; 53 p=p->next[t]; 54 } 55 p->cnt++; 56 } 57 58 void Build_the_fail() 59 { 60 root -> fail = NULL; 61 //node *p=root; 62 queue<node*>q; 63 q.push(root); 64 65 while( !q.empty() ) 66 { 67 node *cur=q.front(); 68 q.pop(); 69 70 for(int i=0; i<26; i++ ) 71 { 72 if( cur->next[i]!=NULL ) 73 { 74 if( cur==root ) 75 cur->next[i]->fail = root; 76 else 77 { 78 node *p=cur->fail; 79 while( p!=NULL ) 80 { 81 if( p->next[i]!=NULL ) 82 { 83 cur->next[i]->fail = p->next[i]; 84 break; 85 } 86 87 else 88 { 89 p = p -> fail; 90 } 91 } 92 if( p==NULL ) 93 cur->next[i]->fail = root; 94 } 95 q.push(cur->next[i]); 96 } 97 } 98 } 99 } 100 101 int query(char *str) 102 { 103 node *cur=root; 104 int pos; 105 int cont=0; 106 int len=strlen(str); 107 for(int i=0; i<len; i++ ) 108 { 109 pos=str[i]-'a'; 110 while( cur->next[pos]==NULL && cur!=root ) 111 { 112 cur = cur->fail; 113 } 114 cur = cur->next[pos]; 115 if( cur==NULL ) 116 cur = root; 117 node *temp=cur; 118 while( temp!=root ) 119 { 120 if( temp->cnt!=-1 ) 121 { 122 cont += temp-> cnt; 123 temp -> cnt = -1; 124 } 125 temp = temp -> fail; 126 } 127 } 128 return cont; 129 } 130 131 int main() 132 { 133 int t; 134 scanf("%d", &t); 135 while( t-- ) 136 { 137 init(); 138 scanf("%d", &n); 139 for(int i=0; i<n; i++ ) 140 { 141 scanf("%s", s); 142 _Insert(s); 143 } 144 Build_the_fail(); 145 scanf("%s", str); 146 printf("%d\n", query(str)); 147 } 148 return 0; 149 }
数组方式:
1 /* */ 2 # include <iostream> 3 # include <algorithm> 4 # include <utility> 5 # include <memory> 6 # include <deque> 7 # include <queue> 8 # include <stack> 9 # include <map> 10 # include <set> 11 # include <list> 12 # include <vector> 13 # include <cassert> 14 # include <functional> 15 # include <bitset> 16 # include <cmath> 17 # include <cstdlib> 18 # include <climits> 19 # include <cstring> 20 # include <string> 21 using namespace std; 22 typedef long long ll; 23 24 # define mem(a,b)(a,b,sizeof(a)) 25 # define lowbit(x)(x&(-x)) 26 # define lcm(a,b)(a*b/__gcd(a,b)) 27 const ll mod=1e9+7; 28 const int maxn=500010; 29 const double pi=acos(-1.0); 30 31 struct ac_auto 32 { 33 int Next[maxn][26], Fail[maxn], End[maxn];//Next[now][buf[i]-'a'] 是now节点存着buf[i]字符的子节点的编号 34 int L, root;//注意这是全局变量,L是编号,root是根节点 35 36 37 int newNode(){ 38 for(int i=0; i<26; i++ )//26叉树 39 Next[L][i] = -1; 40 End[L++] = 0; 41 return L-1;//返回节点编号 42 } 43 44 void Initial(){ 45 L=0; 46 root = newNode(); 47 } 48 49 void Insert( char buf[] ) 50 { 51 int len=strlen(buf); 52 int now = root; 53 for(int i=0; i<len; i++ ) 54 { 55 if( Next[now][buf[i]-'a'] == -1 ) 56 Next[now][buf[i]-'a'] = newNode();//若子节点没有buf[i],则插入buf[i] 57 now = Next[now][buf[i]-'a'];//now是当前节点编号 58 } 59 End[now]++;//作为结束字符的编号+1,计算单词出现的次数 60 } 61 62 void Build_the_fail() 63 { 64 queue<int>ans; 65 Fail[root] = root;//根节点的失败指针指向自己 66 67 for(int i=0; i<26; i++ ) 68 { 69 if( Next[root][i]==-1 ) 70 Next[root][i] = root; 71 else 72 { 73 Fail[Next[root][i]] = root;//根节点的子节点的失败指针指向根节点 74 ans.push(Next[root][i]); 75 } 76 } 77 78 while( !ans.empty() ) 79 { 80 int now = ans.front(); 81 ans.pop(); 82 83 for(int i=0; i<26; i++) 84 { 85 if( Next[now][i]==-1 ) 86 Next[now][i] = Next[Fail[now]][i];//若buf[i]没有插入字典树,则将令其等于其父节点的失败指针指向的节点的子节点buf[i]字符所在的节点 87 else 88 { 89 Fail[Next[now][i]] = Next[Fail[now]][i];//若buf[i]已经插入,其失败指针就指向其父节点的失败指针指向的节点的子节点buf[i]所在的节点 90 ans.push(Next[now][i]);// 91 } 92 } 93 } 94 } 95 96 int Query( char buf[] ) 97 { 98 int now = root;//从根节点开始找,now初始化为root 99 int res = 0;//结果 100 int len = strlen(buf); 101 102 for(int i=0; i<len; i++ ) 103 { 104 now = Next[now][buf[i]-'a'];//当前节点的编号 105 int temp = now; 106 while( temp!=root ) 107 { 108 res += End[temp];//只有找到单词最后一个字符所在位置End[temp]才会>0,否则为0,所以可以直接加 109 End[temp] = 0;//加完后置为0 110 temp = Fail[temp];//拓展到失败指针指向的位置,继续找 111 } 112 } 113 return res; 114 } 115 }AC; 116 117 const int MAXN = 1000010; 118 int T, n; 119 char buf[MAXN]; 120 121 int main() 122 { 123 ios::sync_with_stdio(false); 124 cin>>T; 125 while( T-- ) 126 { 127 cin>>n; 128 AC.Initial(); 129 for(int i=0; i<n; i++ ) 130 { 131 cin>>buf; 132 AC.Insert(buf); 133 } 134 AC.Build_the_fail(); 135 cin>>buf; 136 cout<<AC.Query(buf)<<endl; 137 } 138 return 0; 139 }