AC自动机模板

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

struct node
{
	int fail;
	int next[26];
	int c;
	void newnode(){memset(next,-1,sizeof(next));fail=-1,c=0;}
}a[2000000];
int num;
char s[1000005];

void insert()
{
	int pos=0,t,i;
	for(i=0;s[i];++i)
	{
		t=s[i]-'a';
		if(a[pos].next[t]==-1)
		{
			num++;
			a[num].newnode();
			a[pos].next[t]=num;
		}
		pos=a[pos].next[t];
	}
	a[pos].c++;
}


void KMP() //bfs实现
{
  queue<int> q;
  a[0].fail=-1;
  q.push(0);
  
  while(!q.empty())
  {
     int t=q.front();
     q.pop();	
     for(int i=0;i<26;++i) //寻找子节点
	{
	  int x=a[t].next[i];
	  if(x!=-1) //子节点存在
	 {
		if(t==0)a[x].fail=0; //第一层直接指向根节点
		else
		{
			int p=a[t].fail;//父节点失败指针
			while(p!=-1) 
			{
				if(a[p].next[i]!=-1) //指针指向的子节点与当前相同
				{
					a[x].fail=a[p].next[i];
					break;
				}
			       p=a[p].fail; //往上寻找
			}
			if(p==-1)a[x].fail=0; //父节点失败指针不存在,指向根节点
	 }
	 q.push(x);
    }
   }  
  }
}


int find()
{
	int pos=0,i=0,t,m=0;
	while(s[i])
	{
		t=s[i]-'a';
                while(pos!=0 && a[pos].next[t]==-1)pos=a[pos].fail; //沿失败指针往回走
                pos=a[pos].next[t];
    
               if(pos==-1)pos=0; //回到根节点
    
               int tpos=pos; 
               while(tpos!=0 && a[tpos].c!=-1) //寻找过某个标记点后标记为-1
               {
    	                m+=a[tpos].c;
    	                a[tpos].c=-1;
    	                tpos=a[tpos].fail;
                }
               ++i;
	}
	return m;
}

int main()
{
	freopen("h.in","r",stdin);
	freopen("h.out","w",stdout);
	int T,n;
	scanf("%d",&T);
	while(T--)
	{
		num=0;
		a[0].newnode();
		scanf("%d\n",&n);
		for(int i=1;i<=n;++i)
		{
			gets(s);
			insert();
		}
		gets(s);
		KMP();
		printf("%d\n",find());
	}
	return 0;
}

失败指针:
对于每个节点,我们可以这样处理:设这个节点上的字母为C,沿着他父亲的失败指针走,直到走到一个节点,他的儿子中也有字母为C的节点。然后把当前节点的失败指针指向那个字目也为C的儿子。如果一直走到了root都没找到,那就把失败指针指向root
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值