这个也是模板题
注意开的数据范围,重点是看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;
}

我们只需要先建立所有密码的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;
}
原文:https://www.cnblogs.com/shirlybaby/p/12732669.html