洛谷 P3796 【模板】AC自动机(加强版)(AC自动机)
题目链接:https://www.luogu.com.cn/problem/P3796
AC自动机:复杂度$O( (N+M)\times L )$,N为模式串个数,L为平均长度,M为文章长度。
insert:
构造一个trie,然后标记一下每一个模式串的最后一个,即$vis$。
get_fail:
进行在trie上进行BFS,第一层点的失配指针指向根节点;之后的一个节点失配指针指向/他父亲的失配指针/指向的节点中/的儿子具有相同节点的位置。
这里有一个小优化:fail是用来寻找失配时走到的位置的,走一个点fail的他的ch[i]一定是没有的。那么可以用这些ch指针直接指向它的fail的ch[i],可以发现这样操作之后,每个点的ch[i]直接指向了原本沿着失配边不停走的最终结果,这样的话,匹配时每次用ch指针的对象即可,不用一直沿fail边走看看这个点有没有ch[i]了。
query:
向下一层循环求解,直到fail不能走了,注意如果j不是终点,那么即ans[0]++,但最终不影响结果,因为统计时从1开始...
AC代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 using namespace std; 7 const int N=1000000+100; 8 const int maxn=505; 9 int fail[N],ch[N][26],vis[N]; 10 int ans[N]; 11 string s[maxn]; 12 int cnt; 13 void insert(string s,int v){ 14 int u=0; 15 int len=s.length(); 16 for(int i=0;i<len;i++){ 17 int id=s[i]-'a'; 18 if(!ch[u][id]) ch[u][id]=++cnt; 19 u=ch[u][id]; 20 } 21 vis[u]=v; 22 } 23 void get_fail(){ 24 int now=0; 25 queue<int> q; 26 for(int i=0;i<26;i++){ 27 if(ch[now][i]){ 28 q.push(ch[now][i]); 29 fail[ch[now][i]]=now; 30 } 31 } 32 while(!q.empty()){ 33 int u=q.front(); 34 q.pop(); 35 for(int i=0;i<26;i++){ 36 int v=ch[u][i]; 37 if(v) fail[v]=ch[fail[u]][i],q.push(v); 38 else ch[u][i]=ch[fail[u]][i];//you hua 39 } 40 } 41 } 42 void query(string s){ 43 int now=0; 44 int len=s.length(); 45 for(int i=0;i<len;i++){ 46 now=ch[now][s[i]-'a']; 47 for(int j=now;j;j=fail[j]) ans[vis[j]]++; 48 } 49 } 50 int main(){ 51 int n; 52 while(scanf("%d",&n)){ 53 memset(vis,0,sizeof(vis)); 54 memset(ans,0,sizeof(ans)); 55 memset(ch,0,sizeof(ch)); 56 memset(fail,0,sizeof(fail)); 57 if(n==0) break; 58 for(int i=1;i<=n;i++){ 59 cin>>s[i]; 60 insert(s[i],i); 61 } 62 get_fail(); 63 string str; 64 cin>>str; 65 query(str); 66 int maxx=0; 67 for(int i=1;i<=n;i++) maxx=max(maxx,ans[i]); 68 printf("%d\n",maxx); 69 for(int i=1;i<=n;i++) if(ans[i]==maxx) cout<<s[i]<<endl; 70 } 71 return 0; 72 }