[Trie/AC自动机/AC自动机进阶] AC自动机模板

文章目录

  • T1 [于是他错误的点名开始了](https://www.luogu.com.cn/problem/P2580)
  • T2 [【模板】AC自动机(简单版)](https://www.luogu.com.cn/problem/P3808)
  • T3 [【模板】AC自动机(加强版)](https://www.luogu.com.cn/problem/P3796)
  • T4 [【模板】AC自动机(二次加强版)](https://www.luogu.com.cn/problem/P5357)

T1 于是他错误的点名开始了

#include
using namespace std;
#define in Read()
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=5e5+10;
int n;
int trie[NNN][26],sz;
char s[100];
int mark[NNN];

inline void update(){
	int p=0,len=strlen(s+1);
	for(int i=1;i<=len;++i){
		int c=s[i]-'a';
		if(!trie[p][c]) trie[p][c]=++sz;
		p=trie[p][c];
	}
	++mark[p];
}

inline int query(){
	int p=0,len=strlen(s+1);
	for(int i=1;i<=len;++i){
		int c=s[i]-'a';
		if(!trie[p][c]) return 0;
		p=trie[p][c];
	}
	++mark[p];
	return mark[p]-1;
}

int main(){
	n=in;
	for(int i=1;i<=n;++i){
		scanf("%s",s+1);
		update();
	}
	n=in;
	for(int i=1;i<=n;++i){
		scanf("%s",s+1);
		int val=query();
		if(val==1)puts("OK");
		if(val> 1)puts("REPEAT");
		if(!val  )puts("WRONG");
	}
	return 0;
}

T2 【模板】AC自动机(简单版)

#include
#define in Read()
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e6+10;
int n;
char s[NNN];
int trie[NNN][26],sz;
int val[NNN],fail[NNN],last[NNN];

inline void update(){
	int p=0,len=strlen(s+1);
	for(int i=1;i<=len;++i){
		int c=s[i]-'a';
		if(!trie[p][c]) trie[p][c]=++sz;
		p=trie[p][c];
	}
	++val[p];
}

inline void get_fail(){
	queue<int>q;
	int p=0;
	
	for(int i=0;i<26;++i){
		p=trie[0][i];
		if(!p)continue;
		fail[p]=last[p]=0;
		q.push(p);
	}
	
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<26;++i){
			p=trie[u][i];//j+1
			if(!p){
				trie[u][i]=trie[fail[u]][i];//连成一片 
				continue;
			}
			q.push(p);
			int v=fail[u];//nxt[j+1]
			while(v&&!trie[v][i]) v=fail[v];//while(j&&t[i+1](字符串间kmp就看存不存在)!=t[j+1]) j=nxt[j];
			fail[p]=trie[v][i];//nxt[i]=j(++j)
			last[p]=val[fail[p]]?fail[p]:last[fail[p]];//最大后缀 
		}
	}
}

inline int query(){
	int p=0,len=strlen(s+1);
	int ans=0;
	for(int i=1;i<=len;++i){
		int c=s[i]-'a';
		p=trie[p][c];
		int tmp=0;
		if(val[p])
			tmp=p;
		else if(last[p])
			tmp=last[p];
		while(tmp){
			ans+=val[tmp];
			val[tmp]=0;
			tmp=last[tmp];
		}
	}
	return ans;
}

int main(){
	n=in;
	for(int i=1;i<=n;++i){
		scanf("%s",s+1);
		update();
	}
	get_fail();
	scanf("%s",s+1);
	printf("%d",query());
	return 0;
}

T3 【模板】AC自动机(加强版)

#include
#define in Read()
#define int long long
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

int n;
const int NNN=1e6+10;
char s[200][100],t[NNN];
int sz;
struct ACAM{
	int id,fail,last;
	int son[26];
}ch[NNN];
struct Ans{
	int id,cnt;
	
	friend inline bool operator < (const Ans u,const Ans v){
		if(u.cnt!=v.cnt) return u.cnt>v.cnt;
		return u.id<v.id;
	}
	
}ans[NNN];

inline void Clear(int x){
	memset(ch[x].son,0,sizeof(ch[x].son));
	ch[x].id=ch[x].fail=ch[x].last=0;
}

inline void update(int x){
	int p=0,len=strlen(s[x]+1);
	for(int i=1;i<=len;++i){
		int c=s[x][i]-'a';
		if(!ch[p].son[c]) ch[p].son[c]=++sz,Clear(sz);
		p=ch[p].son[c];
	}
	ch[p].id=x;
}

inline void get_fail(){
	queue<int>q;
	int p=0;
	for(int i=0;i<26;++i){
		p=ch[0].son[i];
		if(!p) continue;
		q.push(p);
		ch[p].last=ch[p].fail=0;
	}
	
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<26;++i){
			p=ch[u].son[i];
			if(!p){
				ch[u].son[i]=ch[ch[u].fail].son[i];
				continue;
			}
			
			q.push(p);
			int v=ch[u].fail;
			while(v&&!ch[v].son[i]) v=ch[v].fail;
			ch[p].fail=ch[v].son[i];
			ch[p].last=ch[ch[p].fail].id?ch[p].fail:ch[ch[p].fail].last;
		}
	}
}

inline void query(){
	int p=0,len=strlen(t+1);
	for(int i=1;i<=len;++i){
		int c=t[i]-'a';
		p=ch[p].son[c];
		int tmp=0;
		if(ch[p].id) tmp=p;
		else if(ch[p].last) tmp=ch[p].last;
		while(tmp){
			ans[ch[tmp].id].cnt+=1;
			tmp=ch[tmp].last;
		}
	}
}

inline void work(){
	for(int i=1;i<=n;++i){
		scanf("%s",s[i]+1);
		update(i);
		ans[i].cnt=0;
		ans[i].id=i;
	}
	get_fail();
	scanf("%s",t+1);
	query();
	sort(ans+1,ans+n+1);
	printf("%lld\n",ans[1].cnt);
	if(ans[1].cnt){
		printf("%s\n",s[ans[1].id]+1);
		for(int i=2;i<=n;++i){
			if(ans[i-1].cnt^ans[i].cnt) break;
			printf("%s\n",s[ans[i].id]+1);
		}
	}
}

signed main(){
	while(true){
		n=in;
		if(!n)break;
		for(int i=1;i<=n;++i)ans[i].id=i;
		sz=0;
		Clear(0);
		work();
	}
	return 0;
}

T4 【模板】AC自动机(二次加强版)

注意判重

  • 法一:last数组优化
#include
#define in Read()
#define int long long
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=5e5+10;
char t[NNN],s[NNN*10];
struct Trie{
	int son[26];
	int fail,last;
	int id;
}ch[NNN];
int sz,ans[NNN],n;
int same[NNN];

inline void update(int x){
	int p=0,len=strlen(t+1);
	for(int i=1;i<=len;++i){
		int c=t[i]-'a';
		if(!ch[p].son[c]) ch[p].son[c]=++sz;
		p=ch[p].son[c];
	}
	if(!ch[p].id) ch[p].id=x;
	else same[x]=ch[p].id;
}

inline void get_fail(){
	queue<int>q;
	int p=0;
	for(int i=0;i<26;++i){
		p=ch[0].son[i];
		if(!p) continue;
		q.push(p);
	}
	
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<26;++i){
			p=ch[u].son[i];
			if(!p){
				ch[u].son[i]=ch[ch[u].fail].son[i];
				continue;
			}
			q.push(p);
			int v=ch[u].fail;
			while(v&&!ch[v].son[i]) v=ch[v].fail;
			ch[p].fail=ch[v].son[i];
			ch[p].last=ch[ch[p].fail].id?ch[p].fail:ch[ch[p].fail].last;
		}
	}
}

inline void query(){
	int p=0,len=strlen(s+1);
	for(int i=1;i<=len;++i){
		int c=s[i]-'a';
		p=ch[p].son[c];
		int tmp=0;
		if(ch[p].id) tmp=p;
		else if(ch[p].last) tmp=ch[p].last;
		while(tmp){
			++ans[ch[tmp].id];
			tmp=ch[tmp].last;
		}
	}
}

signed main(){
	n=in;
	for(int i=1;i<=n;++i){
		scanf("%s",t+1);
		update(i);
	}
	get_fail();
	scanf("%s",s+1);
	query();
	for(int i=1;i<=n;++i){
		if(same[i]) printf("%lld\n",ans[same[i]]);
		else printf("%d\n",ans[i]);
	}
	return 0;
}
  • 法二:拓扑排序优化
    f a i l fail fail树: O ( N 2 ) O(N^2) O(N2)(每一个节点都要跳到叶子节点去)
    拓扑排序后滑: O ( N ) O(N) O(N)(只跳入度 0 0 0的)
    具体方法就是只放入入度为 0 0 0的点,统计之后扔掉,更新它们 f a i l fail fail的入度
#include
#define in Read()
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=2e5+10;
char t[NNN],s[NNN*10];
struct Trie{
	int son[26];
	int fail,into,ans,val;
}ch[NNN];
int sz,m[NNN],n;
//m:记录单词结束点的编号 

inline void update(int x){
	int p=0,len=strlen(t+1);
	for(int i=1;i<=len;++i){
		int c=t[i]-'a';
		if(!ch[p].son[c]) ch[p].son[c]=++sz;
		p=ch[p].son[c];
	}
	++ch[p].val;
	m[x]=p;
}

inline void get_fail(){
	queue<int>q;
	int p=0;
	for(int i=0;i<26;++i){
		p=ch[0].son[i];
		if(!p) continue;
		q.push(p);
	}
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<26;++i){
			p=ch[u].son[i];
			if(!p){
				ch[u].son[i]=ch[ch[u].fail].son[i];
				continue;
			}
			q.push(p);
			int v=ch[u].fail;
			while(v&&!ch[v].son[i]) v=ch[v].fail;
			if(ch[v].son[i]!=p){
				ch[p].fail=ch[v].son[i];
				++ch[ch[v].son[i]].into;
			}
		}
	}
}

inline void query(){
	int p=0,len=strlen(s+1);
	for(int i=1;i<=len;++i){
		int c=s[i]-'a';
		while(p&&!ch[p].son[c]) p=ch[p].fail;//一跳到底,后面的点只与最上面的点有关,可以根据它统计 
		p=ch[p].son[c];
		if(!p)continue;//就是把tmp那很多步变成了一步 
		++ch[p].ans;
	}
}

int main(){
	n=in;
	for(int i=1;i<=n;++i){
		scanf("%s",t+1);
		update(i);
	}
	get_fail();
	scanf("%s",s+1);
	query();
	queue<int>q;
	for(int i=1;i<=sz;++i)
		if(!ch[i].into&&ch[i].fail)
			q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
		ch[ch[u].fail].ans+=ch[u].ans;
		--ch[ch[u].fail].into;
		if(!ch[ch[u].fail].into)
			q.push(ch[u].fail);
	}
	for(int i=1;i<=n;++i)
		printf("%d\n",ch[m[i]].ans);
	return 0;
}

你可能感兴趣的:(#,AC自动机)