多模式匹配--AC自动机算法

 首先简要介绍一下 AC 自动机: Aho-Corasick automation ,该算法在 1975 年产生于贝尔实验室,是著名的多模匹配算法之一。一个常见的例子就是给出 n 个单词,再给出一段包含 m 个字符的文章,让你找出有多少个单词在文章里出现过。要搞懂 AC 自动机,先得有模式树(字典树) Trie 和 KMP 模式匹配算法的基础知识。 AC 自动机算法分为 3 步:构造一棵 Trie 树,构造失败指针和模式匹配过程。

如果你对KMP算法和了解的话,应该知道KMP算法中的next函数(shift函数或者fail函数)是干什么用的。KMP中我们用两个指针i和j分别表示,A[i-j+ 1..i]与B[1..j]完全相等。也就是说,i是不断增加的,随着i的增加j相应地变化,且j满足以A[i]结尾的长度为j的字符串正好匹配B串的前 j个字符,当A[i+1]≠B[j+1],KMP的策略是调整j的位置(减小j值)使得A[i-j+1..i]与B[1..j]保持匹配且新的B[j+1]恰好与A[i+1]匹配,而next函数恰恰记录了这个j应该调整到的位置。同样AC自动机的失败指针具有同样的功能,也就是说当我们的模式串在Tire上进行匹配时,如果与当前节点的关键字不能继续匹配的时候,就应该去当前节点的失败指针所指向的节点继续进行匹配。

代码:

#include <stdio.h>
#include <string.h>


const int MAXQ = 500000+10;
const int MAXN = 1000000+10;
const int MAXK = 26;  //自动机里字符集的大小 
struct TrieNode
{
	TrieNode* fail;
	TrieNode* next[MAXK];
	bool danger;   //该节点是否为某模式串的终结点 
	int  cnt;    //以该节点为终结点的模式串个数 
	TrieNode()
	{
		fail = NULL;
		memset(next, NULL, sizeof(next));
		danger = false;
		cnt = 0;
	}
}*que[MAXQ], *root;
//文本字符串
char msg[MAXN];
int N;
void TrieInsert(char *s)
{
	int i = 0;
	TrieNode *ptr = root;
	while(s[i])
	{
		int idx = s[i] -'a';
		if(ptr->next[idx] == NULL)
			ptr->next[idx] = new TrieNode();
		ptr = ptr->next[idx];
		i++;
	}
	ptr->danger = true;
	ptr->cnt++;
}

void Init()
{
	int  i;
	char  s[100];
	root = new TrieNode();
	printf("insert num: ");
	scanf("%d", &N);
	for(i = 0; i < N; i++)
	{
		printf("insert: ");
		scanf("%s", s);
		TrieInsert(s);
	}
}

void Build_AC_Automation()
{
	int rear = 1, front = 0, i;
	que[0] = root;
	root->fail = NULL;
	while(rear != front)
	{
		TrieNode *cur = que[front++];
		for(i = 0; i < 26; i++)
			if(cur->next[i] != NULL)
			{
				if(cur == root)
					cur->next[i]->fail = root;
				else
				{
					TrieNode *ptr = cur->fail;
					while(ptr != NULL)
					{
						if(ptr->next[i] != NULL)
						{
							cur->next[i]->fail = ptr->next[i];
							if(ptr->next[i]->danger == true)
								cur->next[i]->danger = true;
							break;
						}
						ptr = ptr->fail;
					}
					if(ptr == NULL) cur->next[i]->fail = root;
				}
				que[rear++] = cur->next[i];
			}
	}
}

int AC_Search()
{
	int i = 0, ans = 0;
	TrieNode *ptr = root;
	while(msg[i])
	{
		int  idx = msg[i]-'a';
		while(ptr->next[idx] == NULL && ptr != root) ptr = ptr->fail;
		ptr = ptr->next[idx];
		if(ptr == NULL) ptr = root;
		TrieNode *tmp = ptr;
		while(tmp != NULL)
		{
			ans += tmp->cnt;
		//	tmp->cnt = -1;
			tmp = tmp->fail;
		}
		i++;
	}
	return ans;
}

int main()
{
	int T;
	printf("T: ");
	scanf("%d", &T);
	while(T--)
	{
		Init();
		Build_AC_Automation();
		//文本 
		printf("input msg: ");
		scanf("%s", msg);
		printf("%d\n", AC_Search());
	}
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值