[hdu2222]Keywords Search(AC自动机)
题意:给定n个单词,一个字符串,问字符串中出现了多少个单词。
解题关键:AC自动机模板题,注意根据题意,匹配完成之后要置0.
注意char数组也可以用cin,
注意理解AC自动机,不可能在同一层 出现两个相同的前缀,不然会合并在一起的,所以一定会分层,所以可以依靠fail指针递归寻找。
AC自动机最重要的地方在于 用后缀来匹配前缀
复杂度:$O(NS + T)$
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=26; 5 const int MAXN=520000; 6 struct Trie{//数组形式 7 int Next[MAXN][N],Fail[MAXN],End[MAXN],root,tot;//大小为所以匹配字符串的总和 8 int newnode(){//结构体内部用 9 for(int i=0;i<N;i++) Next[tot][i]=-1; 10 End[tot++]=0; 11 return tot-1; 12 } 13 void init(){ 14 tot=0; 15 root=newnode(); 16 } 17 void insert(char buf[]){ 18 int len=strlen(buf); 19 int now=root;//now是temp指针 20 for(int i=0;i<len;i++){ 21 int k=buf[i]-'a'; 22 if(Next[now][k]==-1) Next[now][k]=newnode();//next数组代表的是下一个字符索引 23 now=Next[now][k]; 24 } 25 End[now]++;//end数组是当前字符串的个数.字典中可能有相同的单词,若只算一次,改为1. 26 } 27 void build(){//构造fail指针,后缀是某些前缀 28 queue<int>que; 29 Fail[root]=root; 30 for(int i=0;i<N;i++){ 31 if(Next[root][i]==-1) Next[root][i]=root; 32 else{ 33 Fail[Next[root][i]]=root; 34 que.push(Next[root][i]); 35 } 36 } 37 while(!que.empty()){//bfs,会将所有的匹配子串都遍历到 38 int now=que.front(); 39 que.pop(); 40 for(int i=0;i<N;i++){ 41 if(Next[now][i]==-1) Next[now][i]=Next[Fail[now]][i]; 42 else{ 43 Fail[Next[now][i]]=Next[Fail[now]][i]; 44 que.push(Next[now][i]); 45 } 46 } 47 } 48 } 49 int query(char buf[]){ 50 int len=strlen(buf),now=root,res=0; 51 for(int i=0;i<len;i++){ 52 now=Next[now][buf[i]-'a']; 53 int temp=now; 54 while(temp!=root){ 55 res+=End[temp]; 56 End[temp]=0;//模式串只在主串中匹配一次就可以了,若匹配次数算作n次,次数不必置0 57 temp=Fail[temp]; 58 } 59 } 60 return res; 61 } 62 }; 63 Trie ac; 64 char buf[1000003]; 65 int main(){ 66 int T,n; 67 ios::sync_with_stdio(0); 68 cin>>T; 69 while(T--){ 70 ac.init(); 71 cin>>n; 72 for(int i=0;i<n;i++){ 73 cin>>buf; 74 ac.insert(buf); 75 } 76 ac.build();//不要忘记build 77 cin>>buf; 78 int ans=ac.query(buf); 79 cout<<ans<<"\n"; 80 } 81 return 0; 82 }