1479:【例题1】Keywords Search
这个也是模板题
注意开的数据范围,重点是看getfail函数和que函数的写法(记住呀)
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<stack> #include<cstdio> #include<queue> #include<map> #include<vector> #include<set> using namespace std; const int maxn=1e4+10; const int M=1e6+10; const int INF=0x3fffffff; typedef long long LL; typedef unsigned long long ull; //AC自动机模板题 char s[M]; int ch[maxn*32][26]; int fail[maxn*32],flag[maxn*32]; int t,n,tot=0; void inti(){ memset(ch,0,sizeof(ch)); memset(flag,0,sizeof(flag)); memset(fail,0,sizeof(fail)); tot=0; } void inse(string s){ int now=0; for(int i=0;i<s.length();i++){ int x=s[i]-'a'; if(!ch[now][x]){ ch[now][x]=++tot; } now=ch[now][x]; } flag[now]++; //这里单词数+1 } void getfail(){ queue<int> q; for(int i=0;i<26;i++){ if(ch[0][i]) { fail[ch[0][i]]=0; q.push(ch[0][i]); } } while(!q.empty()){ int op=q.front(); q.pop(); for(int i=0;i<26;i++){ if(ch[op][i]){ fail[ch[op][i]]=ch[fail[op]][i]; q.push(ch[op][i]); } else ch[op][i]=ch[fail[op]][i]; } } } int que(string s){ int now=0,ans=0; for(int i=0;i<s.size();i++){ now=ch[now][s[i]-'a']; for(int j=now;j&&flag[j]!=-1;j=fail[j]){ ans+=flag[j]; flag[j]=-1; } } return ans; } int main(){ scanf("%d",&t); while(t--){ inti(); scanf("%d",&n); string tmp; for(int i=0;i<n;i++){ cin>>tmp; inse(tmp); } fail[0]=0; getfail(); scanf("%s",s); printf("%d\n",que(s)); } return 0; }
1480:玄武密码
我们只需要先建立所有密码的trie树
再以母串为主串跑一个AC自动机
不过其中还是有一些需要改动的地方
原本字典树中用来记录某个节点是不是字符串结尾的数组不需要,直接删去
我们需要另一个数组来标记哪些点被匹配
跑完ac自动机后从trie树上找最后一个匹配的点即可
优化:由于nxt数组是递归到0的所以只要有一个点被标记过,那么这个点到0的所有点都已经被遍历过直接退出即可
//为什么一个点超时啊。。。。
有的说是SAM好做一些,还不会先留着
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<stack> #include<cstdio> #include<queue> #include<map> #include<vector> #include<set> using namespace std; const int maxn=1e7+10; const int INF=0x3fffffff; typedef long long LL; typedef unsigned long long ull; /* 我们只需要先建立所有密码的trie树 再以母串为主串跑一个AC自动机 不过其中还是有一些需要改动的地方 原本字典树中用来记录某个节点是不是字符串结尾的数组不需要,直接删去 我们需要另一个数组来标记哪些点被匹配 跑完ac自动机后从trie树上找最后一个匹配的点即可 优化:由于nxt数组是递归到0的所以只要有一个点被标记过,那么这个点到0的所有点都已经被遍历过直接退出即可 */ //为什么一个点超时啊。。。。 int ch[maxn][4]; int fail[maxn]; int flag[maxn]; int n,m,tot=0; char ss[maxn]; char sa[100050][150]; int jud(char x){ if(x=='E') return 0; else if(x=='S') return 1; else if(x=='W') return 2; else if(x=='N') return 3; } void inse(string s){ int now=0; for(int i=0;i<s.length();i++){ int x=jud(s[i]); if(!ch[now][x]){ ch[now][x]=++tot; } now=ch[now][x]; } //flag不用加了,因为是用来判断有没有访问过的 } void getfail(){ queue<int> q; for(int i=0;i<4;i++) { if(ch[0][i]) q.push(ch[0][i]); } while(!q.empty()){ int u=q.front();q.pop(); for(int i=0;i<4;i++){ if(ch[u][i]) fail[ch[u][i]]=ch[fail[u]][i],q.push(ch[u][i]); else ch[u][i]=ch[fail[u]][i]; } } } void biaoji(string s){ int u=0; for(int i=0;i<s.length();i++){ int c=jud(s[i]); u=ch[u][c]; for(int j=u;j;j=fail[j]){ if(flag[j]) break; flag[j]=1; } } } int getan(string s){ int u=0,ans=0; for(int i=0;i<s.length();i++){ int c=jud(s[i]); u=ch[u][c]; if(flag[u]) ans=i+1; } return ans; } int main(){ scanf("%d %d",&n,&m); scanf("%s",ss); for(int i=0;i<m;i++){ scanf("%s",sa[i]); inse(sa[i]); } getfail(); biaoji(ss); for(int i=0;i<m;i++){ printf("%d\n",getan(sa[i])); } return 0; }