HDU 2896 病毒侵袭 AC自动机

时间:2021-07-02 06:21:43

这题的字母范围为128.

要注意的是,匹配之后不能将val[tmp] = 0; 因为下一个例子还要使用

#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 = 100010;
//节点个数,一般字符形式的题26个
const int CHILD_NUM = 128;
//特定题目需要
char test[10010];

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+base] = 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];
			int c = *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] = key;
	}
	//建立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)
	{
		int flag[501];
		memset(flag , 0, sizeof(flag));
		res[0] = 0;
		int n = strlen(test);
		int p = 0;
		for(int i = 0; i < n; i++)
		{
			//int next = ID[test[i]];
			int next = test[i];
			int tmp = p = chd[p][next];
			while(val[tmp] != 0 && flag[val[tmp]] == 0)
			{
				res[0]++;
				res[res[0]] = val[tmp];
			//	val[tmp] = 0;
				flag[val[tmp]] = 1; 
				tmp = fail[tmp];
				if(res[0] >= 3)
					return 3;
			}
		}
	 	return res[0];
	}
}AC;

int main() {
	AC.Initialize();
	int n;
	while(scanf("%d", &n) != EOF)
	{
		AC.Reset();
		char temp[201];
		for(int i = 1; i <= n; i ++)
		{
			scanf("%s", temp);
			AC.Insert(temp, i);
		}
		AC.Construct();
		int m;
		scanf("%d", &m);
		int sum = 0;
		for(int i = 1; i <= m; i++)
		{
			scanf("%s", test);
			int result[5];
			int cnt = AC.Work(result);
			if(cnt > 0)
			{
				sum += 1;
				sort(result+1, result+cnt+1);
				printf("web %d:", i);
				for(int j = 1; j <= cnt; j++)
					printf(" %d", result[j]);
				printf("\n");
			}
		}
		printf("total: %d\n", sum);
	}
	return 0;
}