ac自动机模版hdu2222

刚开始想当然的以为是求单词们出现了多少次,事实上是有多少单词出现了

其实,理解了,写这个不难

三步走

1:加单词建Trie

2:get fail和 last数组

3:套文本开始查找,延失配边走即可


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

int n,m,ch[500005][30],val[500005],tot,fail[500005],last[500005];
char wd[55],s[1020000];
bool vis[500005];
inline int idx(char a) {return a-'a';}
void insert(char *s,int len)
{
	int k=0;
	for (int i=0;i<len;i++)
	{
		int id=idx(s[i]);
		if (ch[k][id]==0) ch[k][id]=++tot;
		k=ch[k][id];
	}
	val[k]++;
}
void getfail()//获取fail和last(fail当中有用的部分)数组
{
	queue<int> q;
	for (int i=0;i<26;i++)
	if (ch[0][i])
	{
		q.push(ch[0][i]);
		fail[ch[0][i]]=last[ch[0][i]]=0;
	}
	while (!q.empty())
	{
		int u=q.front(),v;q.pop();
		for (int i=0;i<26;i++)
		if (v=ch[u][i])
		{
			q.push(v);
			int k=fail[u];
			while (!ch[k][i]&&k) k=fail[k];
			if (ch[k][i]) fail[v]=ch[k][i];
			if (val[fail[v]]) last[v]=fail[v];
			else last[v]=last[fail[v]];//如果它失配的那个点有价值,那么等于他,否则等于失配点所对的那个边
		}
	}
}
int find(char *s,int len)
{
	int ans=0,j=0;
	for (int i=0;i<len;i++)
	{
		int id=idx(s[i]);
		while (!ch[j][id]&&j) j=fail[j];
		if (ch[j][id]) j=ch[j][id];
		int k=j;
		while (!vis[k]&&k)//只要这里没有被访问过,且不是原点就继续 
		{
			ans+=val[k];//加上这里的单词数 
			vis[k]=true;//置为已访问 
			k=last[k];//沿着后缀链接走 
		} 
	}
	return ans;
}
void work()
{
	tot=0;
	memset(ch,0,sizeof(ch));
	memset(val,0,sizeof(val));
	memset(fail,0,sizeof(fail));
	memset(last,0,sizeof(last));
	memset(vis,false,sizeof(vis));
	//init
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%s",wd);
		insert(wd,strlen(wd));//插入单词 
	}
	getfail();//获取fail和last数组 
	scanf("%s",s);
	printf("%d\n",find(s,strlen(s)));//查找 
}
int main()
{
	int T;
	scanf("%d",&T);
	while (T--) work();
	return 0;
}






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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值