对于AC自动机的理解,转载注明出自bestsort.cn 说得贼棒,图文并茂
自己的模板
1 int trie[maxn][27]; 2 int cntword[maxn]; 3 int fail[maxn]; 4 int cnt=1; 5 void add(char *s){ 6 it root = 0,l=strlen(s); 7 for(it i=0;i<l;i++){ 8 int next =s[i]-'A'; 9 if(!trie[root][next]) 10 trie[root][next] = cnt++; 11 root = trie[root][next]; 12 } 13 cntword[root]++;//这里没有重复 14 } 15 void getFail(){ 16 queue <int>q; 17 for(it i=0;i<26;i++){ 18 if(trie[0][i]){ 19 fail[trie[0][i]] = 0; 20 q.push(trie[0][i]); 21 } 22 } 23 while(!q.empty()){ 24 int now = q.front(); 25 q.pop(); 26 for(int i=0;i<26;i++){ 27 if(trie[now][i]){ 28 fail[trie[now][i]] = trie[fail[now]][i]; 29 q.push(trie[now][i]); 30 } 31 else 32 trie[now][i] = trie[fail[now]][i]; 33 } 34 } 35 } 36 int query(char *s){ 37 int now = 0,ans = 0,l=strlen(s); 38 for(it i=0;i<l;i++){ 39 if(s[i]<'A' ||s[i]>'Z'){now=0;continue;}//如果字符串有不是A~Z的now归零 40 now = trie[now][s[i]-'A']; 41 for(it j=now;j && cntword[j]!=-1;j=fail[j]){ 42 cntword[j]=-1; 43 ans++;//出现过的字符串,标记 44 } 45 } 46 return ans; 47 }
hdu2896 病毒侵袭(模板题)
题意:
输入n个字符串(n<=500),这n个字符串是指病毒,字符串长度不超过200,字符是ASCII码可见字符.
给m个字符串(长度不超过1e5)表示网址(m<=1000),说这个网址会有多少病毒,最后统计有多少带病毒的网址
样例输入 样例输出
3 web 1: 1 2 3
aaa total: 1
bbb
ccc
2
aaabbbccc
bbaacc
思路:
因为字符是ASCII码可见字符,所以字符范围128,tire长度[200*500][128],用cntword标记病毒序号。
这题hduG++,mle到自闭,然后换了C++就过了,毒毒毒毒瘤。
#include<iostream> #include<map> #include<set> #include<queue> #include<stdio.h> #include<string.h> using namespace std; #define ll long long #define ull unsigned long long #define il inline #define it register int #define inf 0x3f3f3f3f #define lowbit(x) (x)&(-x) #define pii pair<int,int> #define mak(n,m) make_pair(n,m) #define mem(a,b) memset(a,b,sizeof(a)) #define mod 998244353 const int maxn=1e5+10; int trie[maxn][128]; int cntword[maxn]; int fail[maxn]; int cnt=0,tot=0,num=1; map<char,int>mp; void add(char *s,int num1){ it root = 0,l=strlen(s); for(it i=0;i<l;i++){ int next =mp[s[i]]; if(next==0){next=num; mp[s[i]]=num++; } if(!trie[root][next]) trie[root][next] = ++cnt; root = trie[root][next]; } cntword[root]=num1; } void getFail(){ queue <int>q; for(it i=1;i<=num;i++){ if(trie[0][i]){ fail[trie[0][i]] = 0; q.push(trie[0][i]); } } while(!q.empty()){ int now = q.front(); q.pop(); for(int i=1;i<=num;i++){ if(trie[now][i]){ fail[trie[now][i]] = trie[fail[now]][i]; q.push(trie[now][i]); } else trie[now][i] = trie[fail[now]][i]; } } } void query(char *s,int num1){ int now = 0,ans = 0,l=strlen(s); set<int>st; for(it i=0;i<l;i++){ now = trie[now][mp[s[i]]]; for(it j=now;j && cntword[j]!=-1;j=fail[j]){ st.insert(cntword[j]); //cntword[j] = -1; } } if(st.size()){tot++; printf("web %d:",num1); for(auto &i:st){ printf(" %d",i); } printf(" "); } } char s[10010],ss[210]; int main(){ int n,m;mem(cntword,-1); scanf("%d",&n); for(it i=1;i<=n;i++){ scanf("%s",ss); add(ss,i); } getFail(); scanf("%d",&m); for(it i=1;i<=m;i++){ scanf("%s",s); query(s,i); } printf("total: %d ",tot); return 0; }
hdu3065 病毒侵袭持续中(模板题)
题意:
给n个A~Z字符串,字符串长度不超过50(n<=1000),再给一个字符串长度2000000的,里面的字符是ASCII码可见字符,问n出现过几次
样例输入 样例输出
3 AA: 2
AA CC: 1
BB
CC
ooxxCC%dAAAoen....END
思路:
模板套一套,但问题是tire[50*1000][26]是re…,要[1e5][26]
#include<iostream> #include<map> #include<set> #include<queue> #include<stdio.h> #include<string.h> using namespace std; #define ll long long #define ull unsigned long long #define il inline #define it register int #define inf 0x3f3f3f3f #define lowbit(x) (x)&(-x) #define pii pair<int,int> #define mak(n,m) make_pair(n,m) #define mem(a,b) memset(a,b,sizeof(a)) #define mod 998244353 const int maxn=1e5+10; int trie[maxn][27]; int cntword[maxn]; int fail[maxn]; int cnt=0,tot=0,n; void add(char *s,int num1){ it root = 0,l=strlen(s); for(it i=0;i<l;i++){ int next =s[i]-'A'; if(!trie[root][next]) trie[root][next] = ++cnt; root = trie[root][next]; } cntword[root]=num1; } void getFail(){ queue <int>q; for(it i=0;i<26;i++){ if(trie[0][i]){ fail[trie[0][i]] = 0; q.push(trie[0][i]); } } while(!q.empty()){ int now = q.front(); q.pop(); for(int i=0;i<26;i++){ if(trie[now][i]){ fail[trie[now][i]] = trie[fail[now]][i]; q.push(trie[now][i]); } else trie[now][i] = trie[fail[now]][i]; } } } char s1[2000010],ss[1010][55]; void query(char *s){ int now = 0,ans = 0,l=strlen(s); int numm[1010]={0}; for(it i=0;i<l;i++){ if(s[i]<'A' ||s[i]>'Z'){now=0;continue;} now = trie[now][s[i]-'A']; for(it j=now;j && cntword[j]!=-1;j=fail[j]){ numm[cntword[j]]++; //cout<<cntword[j]<<endl; } } for(it i=1;i<=n;i++){ if(numm[i]==0){continue;} printf("%s: %d ",ss[i],numm[i]); } } int main(){ while(~scanf("%d",&n)){ mem(cntword,-1);mem(trie,0); for(it i=1;i<=n;i++){ scanf("%s",ss[i]); add(ss[i],i); } getFail(); scanf("%s",s1); query(s1); } return 0; }
对了对了还有一个hdu2222,我用tire树莽过的题
这里就贴一个代码吧
#include<queue> #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<cmath> #include<string> #include<vector> #include<functional> #pragma warning(disable:4996) using namespace std; const int maxn = 1000010; #define inf 0x3f3f3f3f #define mem(k,b) memset(k,b,sizeof(k)) #define ll long long char s[maxn]; char ss[55]; int k = 1, t, n; int g[240000][26] = { 0 }, vis[240000] = { 0 }; inline void insert(char *w){ int p = 0, l = strlen(w); for (int i = 0; i < l; i++){ int c = w[i] - 'a'; if (!g[p][c]){ g[p][c] = k++; } p = g[p][c]; } vis[p]++; return; } inline int search(char *w){ int l = strlen(w); int sum = 0; for (int j = 0; j < l; j++){ int p = 0; for (int i = j; i < j + 50; i++){ if (i == l){ break; } int c = w[i] - 'a'; if (!g[p][c]){ break; } p = g[p][c]; if (vis[p]){ sum += vis[p]; vis[p] = 0; } } } return sum; } int main() { scanf("%d", &t); while (t--){ k = 1; mem(vis, 0); mem(g, 0); scanf("%d", &n); getchar(); for (int i = 0; i < n; i++){ gets(ss); insert(ss); } gets(s); printf("%d ", search(s)); } return 0; }