欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

AC自动机学习笔记总结

程序员文章站 2024-01-29 13:56:22
...

离第一次讲这个算法已经过去大半年了,我才来填这个坑,蒟蒻无疑了… …
言归正传,这两周认真复习 (开始学习) AC自动机,敲了几遍板子,感觉自己基本上理解了这个算法,所以来写一篇学习笔记,以便将来再次复习 (开始学习)
注:此篇博客仅作为将来复习时用,他人学习请另选高人博客。

前置知识

  • KMP
  • Trie树

简介

处理多字符串匹配问题,常规问题如:给出n个单词TiT_i,再给出一段包含m个字符的文章S,问有多少个单词在文章里出现了以及出现的次数。

思想

  1. 用Trie树存模式串。
  2. 找每个节点的failfail指针,并找出每个节点的lastlast值。
  3. 跑一遍文章字符串,统计个数即可。

重点!!!
我们知道:当节点u匹配失败时,在所有给定的单词中,我们需要找到另一个单
词v,使得它有尽量长的前缀与u的父节点所代表的串(不含u)的后缀相等,令fail[u]=vfail[u]=v
那如何求failfail指针?
首先初始化,将根节点的每个子节点的failfail都指向根节点,即fail[i]=0fail[i]=0
对于其他节点,令这个节点为uu,我们便沿着它父节点的failfail指针走,一直递归,直到找到一个节点vv,它的子节点中存在uu字符,我们就令fail[u]=vfail[u]=v

板子

有一个由n个单词组成的单词库(仅含小写英文字母),给出一篇长度(即字符个数)
为m的文章,请你计算:单词库中有几个单词在文章中出现过。

第1行1个整数n;
在接下来的n行中,每行输入1个单词;
最后一行输入一个字符串,表示文章。

样例输入:
5
she
he
say
shr
her
yasherhs
输出:
3

#include<bits/stdc++.h>
using namespace std;

int tr[1010][30],fail[1000005],last[1000005],sz,n,val[1000005];
char t[1005],s[1000004];

void build(char *t)
{
	int n=strlen(t),u=0;
	for(int i=0;i<n;i++)
	{
		int c=t[i]-'a';
		if(!tr[u][c]) tr[u][c]=sz++;
		u=tr[u][c];
	}
	val[u]++;
}

void getfail()
{
	queue<int>q;
	fail[0]=0;
	int u=0;
	for(int i=0;i<26;i++)
	{
		u=tr[0][i];
		if(u) {q.push(u);fail[u]=0;last[u]=0;}
	}
	while(!q.empty())
	{
		int r=q.front();q.pop();
		for(int i=0;i<26;i++)
		{
			u=tr[r][i];
			if(!u)
			{
				tr[r][i]=tr[fail[r]][i];
				continue;
			}
			q.push(u);
			int v=fail[r];
			while(v&&!tr[v][i]) v=fail[v];
			fail[u]=tr[v][i];
			last[u]=val[fail[u]] ? fail[u] : last[fail[u]];
		}
	}
}

int find(char *s)
{
	int u=0,cnt=0;
	n=strlen(s);
	for(int i=0;i<n;i++)
	{
		int c=s[i]-'a';
		u=tr[u][c];
		int temp=0;
		if(val[u]) temp=u;
		else if(last[u]) temp=last[u];
		while(temp)
		{
			cnt+=val[temp];
			val[temp]=0;
			temp=last[temp];
		}
	}
	return cnt;
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",t);
		build(t);
	}
	getfail();
	scanf("%s",s);
	int ans=find(s);
	printf("%d",ans);
	return 0;
}

撒花完结!!!

相关标签: AC自动机 总结