AC自动机习题(一本通)

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;
}

  

猜你喜欢

转载自www.cnblogs.com/shirlybaby/p/12732669.html