数据结构与算法系列----AC自己主动机

一:概念

首先简要介绍一下AC自己主动机:Aho-Corasick automation,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法之中的一个。一个常见的样例就是给出n个单词,再给出一段文章(长度是m),让你找出有多少个单词在文章里出现过。

要搞懂AC自己主动机。先得有字典树Trie的基础知识(也有人说需要KMP的知识,我认为暂且不要理会这个。

可是在看这篇文章之前,Trie字典树,你是必需要先搞懂,假设你还不理解Trie,请參考http://blog.csdn.net/laojiu_/article/details/50838421)。

与其它字符匹配不同,KMP算法是单模式串的字符匹配算法,AC自己主动机是多模式串的字符匹配算法。匹配时间复杂度是O(N)。线性复杂度!

二:算法过程(三步走)

举个样例,假如如今给出5个模式串:say she shr he her

主串是:yasherhs

如今问你,这5个模式串有几个出如今主串里的?

OK,如今就拿这个样例来完毕这个算法的过程。

第一步:构建Trie树,这非常easy的了。

构建好后。出现下图:

第二步:构建失败指针

构建失败指针是AC自己主动机的核心所在,玩转了它也就玩转了AC自己主动机,失败指针就是。当我的主串在trie树中进行匹配的时候,假设当前节点不能再继续进行匹配。那么我们就会走到当前节点的fail节点继续进行匹配。

构造失败指针的过程概括起来就一句话:对于root的儿子节点。fail指针直接指向root,其它的全部节点(用到了BFS和队列),设这个节点上的字母为C。沿着它父亲的失败指针走。直到走到一个节点,它的儿子中也有字母为C的节点。然后把当前节点的失败指针指向那个字母为C的节点。

假设一直走到了root都没找到,那就把失败指针指向root。

构建好后,例如以下图:

针对图中红线的”h。e“这两个节点。我们想起了什么呢?对”her“中的”e“来说,e到root距离的n个字符恰好与”she“中的e向上的n个字符相等。

第三步:模式匹配

匹配过程分两种情况:

(1)  当前字符匹配成功,表示从当前节点沿着树边有一条路径能够到达目标字符,此时仅仅需沿该路径走向下一个节点继续匹配就可以,目标字符串指针移向下个字符继续匹配;

(2)  当前字符不匹配,则去当前节点失败指针所指向的字符继续匹配,匹配过程随着指针指向root结束。反复这2个过程中的随意一个。直到模式串走到结尾为止。

注意:主串全部字符在匹配完后都必需要走fail节点来结束自己的旅途,相当于一个回旋,这样做的目的防止包括节点被忽略掉。

见下图,比方:我匹配到了"she",必定会匹配到该字符串的后缀”he",要想在程序中匹配到,则必须节点要走失败指针来结束自己的旅途。

三:完整代码

#include<iostream>
#include<queue>

#define MAX 26//如果仅仅出现26个小写英文字母
#define ROW 4
#define COLUMN 10

using namespace std;

char pattern[ROW][COLUMN] = { "nihao","hao","hs","hsr" };
char *s = "sdmfhsgnshejfgnihaofhsrnihao";

struct Node
{
	int index;//存储模式串的下标
	char x;
	Node *parent;
	Node *next[MAX];
	Node *fail;
	Node()
	{
		index = -1;//pattern数组下标从0開始,-1代表该节点不是单词结尾
		fail = nullptr;
		parent = nullptr;
		for (int i = 0; i < MAX; i++)
			next[i] = nullptr;
	}
};

class ACTree
{
public:
	Node *root;
	ACTree() { root = new Node; root->fail = root; }

	void Add(const char *ch, int index);              //第一步
	void NodeToQueue(Node *node, queue<Node*> &q);    //
	void BuildFailPointer();                          //第二步
	void ACSearch(const char *s);                     //第三步
};

int main()
{
	ACTree tree;

	for (int i = 0; i < ROW; i++)
		tree.Add(pattern[i], i);

	tree.BuildFailPointer();

	cout << "待匹配字符串为(依次5个一组的输出):\n";
	for (int i = 1; i <= strlen(s); i++)
	{
		cout << s[i];
		if (i % 5 == 0)
			cout << "  ";
	}
	cout << endl << endl;

	cout << "匹配结果例如以下:\n";
	cout << "位置\t" << "编号\t" << "模式\n";

	tree.ACSearch(s);

	return 0;
}

void ACTree::Add(const char *ch,int index)
{
	int len = strlen(ch);
	if (len == 0) return;

	Node *p = root;

	for (int i = 0; i < len; i++)
	{
		int k = ch[i] - 'a';

		if (p->next[k] == nullptr)
		{
			p->next[k] = new Node;
			p->next[k]->parent = p;
			p->next[k]->x = ch[i];
		}

		p = p->next[k];
	}

	p->index = index;//注意,在此保证输入的模式串不反复,否则index会被覆盖
}

void ACTree::NodeToQueue(Node *node, queue<Node*> &q)
{
	if (node != nullptr)
	{
		for (int i = 0; i < MAX; i++)
		{
			if (node->next[i])
				q.push(node->next[i]);//不知道这是干嘛的??想想BFS层次遍历的那些事
		}
	}
}

void ACTree::BuildFailPointer()
{
	queue<Node*> q;

	for (int i = 0; i < MAX; i++)
	{
		if (root->next[i])
		{
			NodeToQueue(root->next[i], q);//注意。切不可写成q.push(root->next[i]);
			root->next[i]->fail = root;
		}
	}

	Node *parent, *p;
	char ch;
	while (!q.empty())
	{
		p = q.front();
		ch = p->x;
		parent = p->parent;
		q.pop();
		NodeToQueue(p, q);

		while (1)
		{
			if (parent->fail->next[ch - 'a'] != nullptr)
			{
				p->fail = parent->fail->next[ch - 'a'];
				break;
			}
			else
			{
				if (parent->fail == root)
				{
					p->fail = root;
					break;
				}
				else
					parent = parent->fail->parent;
			}
		}
	}
}

void ACTree::ACSearch(const char *s)
{
	int len = strlen(s);
	if (len == 0) return;

	Node *p = root;

	int i = 0;
	while (i < len)
	{
		int k = s[i] - 'a';

		if (p->next[k] != nullptr)
		{
			p = p->next[k];

			if (p->index != -1)
				cout << i - strlen(pattern[p->index]) + 1 << "\t" << p->index << "\t" << pattern[p->index] << endl;

			i++;
		}
		else
		{
			if (p == root)
				i++;
			else
			{
				p = p->fail;
				if (p->index != -1)
					cout << i - strlen(pattern[p->index]) + 1 << "\t" << p->index << "\t" << pattern[p->index] << endl;
			}
		}
	}

	while (p != root)
	{
		p = p->fail;
		if(p->index!=-1)
			cout << i - strlen(pattern[p->index]) + 1 << "\t" << p->index << "\t" << pattern[p->index] << endl;
	}
}

四:数据測试

返回文件夹---->数据结构与算法文件夹

时间: 2024-10-06 06:52:02

数据结构与算法系列----AC自己主动机的相关文章

字符串算法之 AC自己主动机

近期一直在学习字符串之类的算法,感觉BF算法,尽管非常easy理解,可是easy超时,全部就想学习其它的一些字符串算法来提高一下,近期学习了一下AC自己主动机.尽管感觉有所收获,可是还是有些朦胧的感觉,在此总结一下,希望大家不吝赐教. 一.AC自己主动机的原理: Aho-Corasick automaton.该算法在1975年产生于贝尔实验室,是著名的多模匹配算法之中的一个. 一个常见的样例就是给出N个单词,在给出一段包括m个字符的文章,让你找出有多少个单词在这文章中出现过,.要搞懂AC自己主动

POJ 1204 Word Puzzles AC自己主动机题解

AC自己主动机的灵活运用,本题关键是灵活二字. 由于数据不是非常大.时间要求也不高的缘故.所以本题有人使用暴力法也过了.有人使用Trie.然后枚举八个方向,也过了.只是速度非常慢. 当然有人使用AC自己主动机没AC的,在讨论区里喊AC自己主动机超时的,那是由于不会灵活运用.或者是硬套模板的,AC了速度也不会快. 给出本人的算法思路: 1 把须要查找的keyword建立Trie, 然后构造AC自己主动机 2 查找的时候分八个方向查找,比方棋盘是board[N][M].那么就能够循环i(0->N-1

数据结构与算法系列 目录

最近抽空整理了"数据结构和算法"的相关文章.在整理过程中,对于每种数据结构和算法分别给出"C"."C++"和"Java"这三种语言的实现:实现语言虽不同,但原理如出一辙.因此,读者在了解和学习的过程中,择其一即可! 下面是整理数据数据和算法的目录表,对于每一种按照C/C++/Java进行了划分,方便查阅.若文章有错误或纰漏,请不吝指正.谢谢! 数据结构和算法目录表   C C++ Java 线性结构 1. 数组.单链表和双链表

【UVA】11468-Substring(AC自己主动机)

AC自己主动机的题,须要注意的,建立失配边的时候,假设结点1失配边连到的那个结点2,那个结点2是一个单词的结尾,那么这个结点1也须要标记成1(由于能够看成,这个结点包括了这个单词),之后在Trie树上进行行走,每次走到下一个能够走的结点. 14378527 11468 Substring Accepted C++ 0.585 2014-10-19 10:35:00 #include<cstdio> #include<cstring> #include<algorithm>

hdoj 2222 Keywords Search 【AC自己主动机 入门题】 【求目标串中出现了几个模式串】

Keywords Search Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 44687    Accepted Submission(s): 14103 Problem Description In the modern time, Search engine came into the life of everybody li

【UVA】1449-Dominating Patterns(AC自己主动机)

AC自己主动机的模板题.须要注意的是,对于每一个字符串,须要利用map将它映射到一个结点上,这样才干按顺序输出结果. 14360841 1449 option=com_onlinejudge&Itemid=8&page=show_problem&problem=4195" style="font-size:13.3333330154419px; margin:0px; padding:0px; color:rgb(153,0,0); text-decoratio

HDU - 3341 Lost&amp;#39;s revenge(AC自己主动机+DP)

Description Lost and AekdyCoin are friends. They always play "number game"(A boring game based on number theory) together. We all know that AekdyCoin is the man called "nuclear weapon of FZU,descendant of Jingrun", because of his talen

HDU 2222 Keywords Search(AC自己主动机模板题)

题意:给出一个字符串和若干个模板,求出在文本串中出现的模板个数. 思路:由于有可能有反复的模板,trie树权值记录每一个模板出现的次数就可以. #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<algorithm> #include<vector> #include<map&

ZOJ - 3228 Searching the String (AC自己主动机)

Description Little jay really hates to deal with string. But moondy likes it very much, and she's so mischievous that she often gives jay some dull problems related to string. And one day, moondy gave jay another problem, poor jay finally broke out a