hdu 2222 Keywords Search AC自动机模板题

点击打开链接

题意:给出母串S 长度小于1e6 ,和字典, 问s中出现的dic单词有多少个?

KMP是当字典中单词个数为1的情况 若单词个数很多 则复杂都O(n*m)显然不行 于是AC自动机就来啦~

参考

AC自动机=KMP+Trie
AC自动机主要就3个部分:    
 1 把所有模板插入到Trie中,建立大的状态转移图
 2 建立失败指针:

    根结点的失败指针指向自己
    每个结点的失败指针是从父节点的失败指针指向的结点的子结点中寻找与这个结点相同的结点

     和kmp一样:后缀失配时找到合适的前缀继续匹配

同时 顺手把不存在的边都给补上,顺手建last数组 

last作用:若到i匹配成功,将母串中以后缀i结尾的单词进行快速匹配
当母串中以后缀i的和某个模式串S匹配成功,则该模式串沿着失配边,找到前缀为模式串T,T和后缀s相同,同样也和母串后缀i相同 


3 模式匹配:因为失配函数的时候顺手把不存在的边都给补上了,直接顺着失配边走下去就好了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+20; 
struct Aho{
	int chd[N][26],v[N],f[N],last[N],sz,ans;
	void init()
	{
		sz=1;ans=0;
		memset(v,0,sizeof(v));
		memset(f,0,sizeof(f));
		memset(chd[0],0,sizeof(chd[0]));
	}
	void insert(char *s)
	{
		int u=0,i=0;
		while(s[i])
		{
			int id=s[i]-'a';
			if(!chd[u][id])
			{
				memset(chd[sz],0,sizeof(chd[sz]));
				chd[u][id]=sz++;
			}
			u=chd[u][id];
			i++; 
		}
		v[u]++;
	}
	void getFail()
	{
		queue<int> q;
		f[0]=0;
		for(int c=0;c<26;c++)
		{
			int u=chd[0][c];
			if(u)
			{
				f[u]=0; q.push(u);last[u]=0;
			}
		}
		while(!q.empty())
		{
			int r=q.front();q.pop();
			for(int c=0;c<26;c++)
			{
				int u=chd[r][c];
				if(!u)
				{
					chd[r][c]=chd[f[r]][c];
					continue;
					//补边 
				}
				q.push(u);
				int x=f[r];
				//找到前缀i和后缀c相等 
				while(x&&!chd[x][c]) 
					x=f[x];
				f[u]=chd[x][c];
				//last作用:若到i匹配成功,将母串中以后缀i结尾的单词进行快速匹配
				//当母串中以后缀i的和某个模式串S匹配成功,则该模式串沿着失配边,找到前缀为模式串T,T和后缀s相同,同样也和母串后缀i相同 
				last[u]=v[f[u]]? f[u]:last[f[u]];
			}
		}
	}
	void solve(int j)
	{
		if(!j) return;
		if(v[j])
		{
			ans+=v[j];
			v[j]=0;
		}
		solve(last[j]);
	}
	void find(char *s)
	{
		int n=strlen(s),j=0;
		getFail();
		for(int i=0;i<n;i++)
		{
			int id=s[i]-'a';
			j=chd[j][id];//trie
			if(v[j])
			solve(j);//匹配成功 看还有没有其他串和后缀i匹配
			else if(last[j])
			solve(last[j]);// 
		}
	}	
}aho;
char s[N];
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		aho.init();
		char dic[100];
		int n;
		cin>>n;
		for(int i=0;i<n;i++)
		{
			scanf("%s",dic);
			aho.insert(dic);
		}
		scanf("%s",s);
		aho.find(s);
		cout<<aho.ans<<endl;
	}

	return 0;
} 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值