[Trie/AC自動機/AC自動機進階] AC自動機模板

T1 於是他錯誤的點名開始了

#include<bits/stdc++.h>
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<bits/stdc++.h>
#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<bits/stdc++.h>
#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<bits/stdc++.h>
#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;
}
  • 法二:拓撲排序優化
    failfail樹:O(N2)O(N^2)(每一個節點都要跳到葉子節點去)
    拓撲排序後滑:O(N)O(N)(只跳入度00的)
    具體方法就是隻放入入度爲00的點,統計之後扔掉,更新它們failfail的入度
#include<bits/stdc++.h>
#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章