学习笔记第二十七节:AC自动机

版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢 https://blog.csdn.net/Deep_Kevin/article/details/83893949

正题

      听说NOIP要考,所以临时补了一下,多了一种思考方式。

      AC自动机是基于KMP和字典树的,要想透彻了解AC自动机,最好先学KMP和字典树。

      那么,AC自动机是什么样子的呢?又是用来解决什么问题的呢?

      【模板】AC自动机(简单版),可以以这题为例。

      好像KMP是可以做的,试一下,发现时间复杂度会爆炸,因为文本串要遍历N次。

      通过磨题解,我们学习新算法。

      AC自动机。

      首先我们把n个模式串插入一个字典树中,接着,神奇的事情发生了。

      我们对于字典树中的每一个节点,我们找其对应的fail指针。

      fail指针指的是什么?从根到该节点的路径很明显构成一个字符串。

      那么fail指针指向的就是这个字符串,在字典树中出现的最长的后缀。如图。

扫描二维码关注公众号,回复: 3997634 查看本文章

      是不是很丑,因为fail指针很好理解。

      接着怎么解决这一题呢?

      先把AC自动机建出来,给每个字符串的结尾所对应的节点权值+1.

      然后拿文本串上去跳,让它从根节点一直往下走(按照字符),如果一个节点没有对应的字符,就不断跳fail,直到跳到根。

      如果当前节点是一个模式串的结尾,那么ans+这个节点的权值,并且给这个节点打一个标记,表示记录过了。(不计重复)

      同时要加上fail指针的答案,因为满足这个点一定满足fail,fail指针是我的后缀啊。

#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

int n;
struct node{
	int t,son[26],fail;
	bool tf;
}s[1000010];
char ch[1000010];
int tot=0;
queue<int> f;

void insert(){
	int l=strlen(ch+1);
	int x=0;
	for(int i=1;i<=l;i++){
		int temp=ch[i]-'a';
		if(s[x].son[temp]==-1){
			s[x].son[temp]=++tot;
			s[tot].t=0;memset(s[tot].son,-1,sizeof(s[tot].son));
		}
		x=s[x].son[temp];
	}
	s[x].t++;
}

void get_fail(){
	f.push(0);	
	while(!f.empty()){
		int x=f.front();f.pop();
		for(int i=0;i<26;i++){
			int y=s[x].son[i];
			if(y==-1) continue;
			int now=s[x].fail;
			while(now!=0 && s[now].son[i]==-1) now=s[now].fail;
			if(x!=0 && s[now].son[i]!=-1) s[y].fail=s[now].son[i];
			f.push(y);
		}
	}
}

int get_ans(){
	int ans=0;
	int x=0,l=strlen(ch+1);
	for(int i=1;i<=l;i++){
		int temp=ch[i]-'a';
		while(x!=0 && s[x].son[temp]==-1) x=s[x].fail;
		if(s[x].son[temp]!=-1) x=s[x].son[temp];
		int now=x;
		while(now!=0 && !s[now].tf) ans+=s[now].t,s[now].tf=true,now=s[now].fail;
	}
	return ans;
}

int main(){
	scanf("%d",&n);
	memset(s[0].son,-1,sizeof(s[0].son));
	for(int i=1;i<=n;i++) {
		scanf("%s",ch+1);
		insert();
	}
	scanf("%s",ch+1);
	get_fail();
	printf("%d",get_ans());
}

猜你喜欢

转载自blog.csdn.net/Deep_Kevin/article/details/83893949