AC自动机学习总结

久闻AC自动机的大名,终于,在准备好KMP和字典树之后,开始学习这个看起来高大上的算法了。

多余的写题的时候在补充吧,学完之后发现他的板子并不难,理解也不算太难,网上有很多种写法,近期研究一下,一些代码的常数的问题。洛谷有道题,直接就是板子:

参考不知名大佬的板子(看了好多人的板子了),然后放弃指针,因为觉得要申请空间,好慢啊。

P3808:https://www.luogu.org/problemnew/show/P3808

AC:

// luogu-judger-enable-o2
//#pragma comment(linker, "/STACK:1024000000,1024000000")
  
#include<stdio.h>
#include<string.h> 
#include<math.h> 
   
//#include<map>  
#include<set>
#include<deque> 
#include<queue> 
#include<stack> 
#include<bitset>
#include<string> 
#include<fstream>
#include<iostream> 
#include<algorithm> 
using namespace std; 
  
#define ll long long 
#define Pair pair<int,int>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
//#define clean(a,b) memset(a,b,sizeof(a))// 水印
//std::ios::sync_with_stdio(false);
//  register
const int MAXN=5e6+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const int mod=998244353;
const double PI=acos(-1.0);
struct node{
	int cnt,fail;
	int nxt[26];
}trie[MAXN];
int que[MAXN];
char s[MAXN];
int ecnt=0;
void Insert(char *s){//获得字典树 
	int id=0,len=strlen(s);
	for(int i=0;i<len;++i){
		int Index=s[i]-'a';
		if(trie[id].nxt[Index]==0)
			trie[id].nxt[Index]=++ecnt;
		id=trie[id].nxt[Index];
	}
	trie[id].cnt++;
}
void get_fail(){//获得fail回溯 
	queue<int> que;
	while(que.size())	que.pop();
	for(int i=0;i<26;++i){
		int Index=trie[0].nxt[i];
		if(Index){
			que.push(Index);
			trie[Index].fail=0;
		}
	}
	while(que.size()){
		int tmp=que.front();que.pop();
		for(int i=0;i<26;++i){
			int Index=trie[tmp].nxt[i];
			if(Index==0){//这里如果没有后面的字符的话,将它与其他的有效字符连起来 
				trie[tmp].nxt[i]=trie[trie[tmp].fail].nxt[i];
				continue;
			}//下面就可以直接连接fail了。 
			trie[Index].fail=trie[trie[tmp].fail].nxt[i];
			que.push(Index);
		}
	}
}
//void get_fail(){
//	int head=0,tail=1;
//	for(int i=0;i<26;++i){
//		int Index=trie[0].nxt[i];
//		if(Index){
//			que[tail++]=Index;
//			trie[Index].fail=0;
//		}
//	}
//	while(head<tail){
//		int tmp=que[head++];
//		for(int i=0;i<26;++i){
//			int Index=trie[tmp].nxt[i];
//			if(Index==0){//失配 
//				trie[tmp].nxt[i]=trie[trie[tmp].fail].nxt[i];
//				continue;
//			}
//			trie[Index].fail=trie[trie[tmp].fail].nxt[i];
//			que[tail++]=Index;
//		}
//	}
//}
int AC_automation(char *s){//判定 
	int id=0,ans=0,len=strlen(s);
	for(int i=0;i<len;++i){
		int Index=trie[id].nxt[s[i]-'a'];
		while(Index&&trie[Index].cnt!=-1){
			ans+=trie[Index].cnt;
			trie[Index].cnt=-1;
			Index=trie[Index].fail;
		}
		id=trie[id].nxt[s[i]-'a'];
	}
	return ans;
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%s",s);
        Insert(s);
    }
    get_fail();
    scanf("%s",s);
    printf("%d\n",AC_automation(s));
}


/*
4 2
abcd
bce
abd
cd

abcdefg
*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值