HDU 2222 AC自动机经典题目

本文详细介绍了一种基于AC自动机的数据结构实现方法,并通过具体代码示例展示了如何构建AC自动机,包括初始化、插入字符串、构建状态转移及计算失败指针等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

应用模板,对模板有更深入的理解。

1.       当前结点的失败指针

等于
      父节点的失败指针指向的结点的的同个字母儿子结点的指针

 

2. v = chd[ fail[u] ][i];这句为了别的结点计算败者指针提供便利。无其他作用。

3. work在结点之间的转移方式

 

#include <cstdio>
#include <cstdlib>
#include <string>
#include <climits>
#include <iostream>
#include <vector>
#include <set>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <sstream>
#include <map>
#include <cstring>
#include <queue>
using namespace std;

//MAX_NODE = StringNumber * StringLength
const int MAX_NODE = 500010;
//节点个数,一般字符形式的题26个
const int CHILD_NUM = 26;
//特定题目需要
char test[1000010];

class ACAutomaton {
private:
	//每个节点的儿子,即当前节点的状态转移
	int chd[MAX_NODE][CHILD_NUM];
	//记录题目给的关键数据
	int val[MAX_NODE];
	//传说中的fail指针
	int fail[MAX_NODE];
	//队列,用于广度优先计算fail指针
	int Q[MAX_NODE];
	//字母对应的ID
	int ID[128];
	//已使用节点个数
	int sz;
public:
	//初始化,计算字母对应的儿子ID,如:'a'->0 ... 'z'->25
	void Initialize() {
		fail[0] = 0;
		for (int i = 0 ; i < CHILD_NUM ; i ++) {
			ID[i+'a'] = i;
		}
	}
	//重新建树需先Reset
	void Reset() {
		memset(chd[0] , 0 , sizeof(chd[0]));
		sz = 1;
	}
	//将权值为key的字符串a插入到trie中
	void Insert(char *a,int key) {
		int p = 0;
		for ( ; *a ; a ++) {
			int c = ID[*a];
			if (!chd[p][c]) {
				memset(chd[sz] , 0 , sizeof(chd[sz]));
				val[sz] = 0;
				chd[p][c] = sz ++;
			}
			p = chd[p][c];
		}
		val[p]++;
	}
	//建立AC自动机,确定每个节点的权值以及状态转移
	void Construct() {
		int *s = Q , *e = Q;
		for (int i = 0 ; i < CHILD_NUM ; i ++) {
			if (chd[0][i]) {
				fail[ chd[0][i] ] = 0;
				*e ++ = chd[0][i];
			}
		}
		while (s != e) { /*广度优先。 关键*/
			int u = *s++;
			for (int i = 0 ; i < CHILD_NUM ; i ++) {
				int &v = chd[u][i];
				if (v) {
					*e ++ = v;
					 /*当前结点的失败指针 等于 
					 父节点的失败指针指向的结点的的同个字母儿子结点的指针*/
					fail[v] = chd[ fail[u] ][i]; 
					//以下一行代码要根据题目所给val的含义来写
		//			val[v] |= val[ fail[v] ];
				} else {
					v = chd[ fail[u] ][i];
				}
			}
		}
	}
	//解题,特定题目需要
	int Work()
	{
		int res = 0;
		int n = strlen(test);
		int p = 0;
		for(int i = 0; i < n; i++)
		{
			int next = ID[test[i]];
			int tmp = p = chd[p][next];
			while(val[tmp] != 0)
			{
				res += val[tmp];
				val[tmp] = 0;
				tmp = fail[tmp];
			}
		}
	 	return res;
	}
}AC;

int main() {
	int t ;
	cin >> t ;
	AC.Initialize();
	while(t--)
	{
		AC.Reset();
		int n;
		cin >> n;
		char temp[51];
		while(n--)
		{
			scanf("%s", temp);
			AC.Insert(temp, 1);
		}
		AC.Construct();
		scanf("%s", test);
		printf("%d\n", AC.Work());
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值