用AC自动机(AC automatic)得用到Trie树
Trie树结合AC自动机的数据结构(就多个Fail指针):
class Trie{
private:
size_t count; // 统计单词前缀出现的次数
size_t flag; //是否成单词的标志位
Trie* fail; //构造AC自动机的Fail指针
Trie **next; // 指向各个子树的指针
};
Trie树包括 插入、查找、销毁三种基本运算
AC自动机是在Trie树特性进行Fail指针的构造,包括 构造Fail指针函数和多模匹配的函数(Trie树实现,见上篇文章)
含义用途都写在代码注释中了:
#include"Trie.h"
#include <queue>//用BFS方式建立Fail指针
class ACAutomatic
{
public:
explicit ACAutomatic(Trie *&_trie) : trie(_trie), typeCase(trie->typeCase) { makeFail(); }
ACAutomatic() = delete;
~ACAutomatic() = default;
/**
构造失败指针Fail的过程概括:
设这个节点上的字母为C,沿着他父亲的失败指针走,直到走到一个节点,他的儿子中也有字母为C的节点。
然后把当前节点的失败指针指向那个字母也为C的儿子。如果一直走到了root都没找到,那就把失败指针指向root。
使用BFS(广度优先搜索),层次遍历节点来处理,每一个节点的失败路径。
*/
void makeFail();
/**
从root节点开始,每次根据读入的字符沿着自动机向下移动。
当读入的字符,在分支中不存在时,递归走失败路径。如果走失败路径走到了root节点,则跳过该字符,处理下一个字符。
因为AC自动机是沿着输入文本的最长后缀移动的,所以在读取完所有输入文本后,最后递归走失败路径,直到到达根节点,这样可以检测出所有的模式。
*/
int SearchACTrie(const std::string &strings) const;
private:
Trie *≜
TypeCase &typeCase;
std::queue<Trie*> trieQueue;
};
inline void ACAutomatic::makeFail()
{
auto root = trie;
trie->fail = trie;
trieQueue.push(trie);
while (trieQueue.size() != 0)
{
auto rootNode = this->trieQueue.front();
for (auto beg(0); beg < typeCase; ++beg) {
if (rootNode->next[beg]) {
trieQueue.push(rootNode->next[beg]);
if (rootNode->fail->next[beg] && rootNode->fail->next[beg] != rootNode->next[beg])
rootNode->next[beg]->fail = rootNode->fail->next[beg];
else rootNode->next[beg]->fail = rootNode->fail;
}
}
trieQueue.pop();
}
}
inline int ACAutomatic::SearchACTrie(const std::string &strings) const
{
auto strIteratorToInt(0);
auto triethis(trie);
auto cnt(0);//统计单词出现次数
std::string::const_iterator stringBegin(strings.begin());
std::string::const_iterator stringEnd(strings.end());
while (stringBegin != stringEnd)
{
switch (typeCase)
{
case 2: strIteratorToInt = static_cast<size_t>(*stringBegin) - 48;//限制非法输入
if (strIteratorToInt < 0 || strIteratorToInt>1) return false;
break;
case 10:
strIteratorToInt = static_cast<size_t>(*stringBegin) - 48;//限制非法输入
if (strIteratorToInt < 0 || strIteratorToInt>9) return false;
break;
case 26:strIteratorToInt = static_cast<size_t>(*stringBegin) - 97;//限制非法输入
if (strIteratorToInt < 0 || strIteratorToInt>26) return false;
break;
default:return false;
}
if (triethis->next[strIteratorToInt]) {
triethis = triethis->next[strIteratorToInt];
if (triethis->flag)
cnt+= triethis->flag;
}
else {
triethis = triethis->fail;
if(triethis->next[strIteratorToInt])
triethis = triethis->next[strIteratorToInt];
if (triethis->flag)
cnt += triethis->flag;
}
++stringBegin;
}
return cnt;
}
例:
在yasherhs中查找say、she、shr、her、sh包含的个数
(含重,如:sher 包含she和her两个单词)
#include<iostream>
#include"Trie.h"
#include"ACAutomatic.h"
int main()
{
auto trie = new Trie(LowerCase);//构造Trie树,表示用小写字母类型存储
std::cout << trie->InsertTrie("say");//插入成功返回true
std::cout << trie->InsertTrie("she");//插入成功返回true
std::cout << trie->InsertTrie("shr");//插入成功返回true
std::cout << trie->InsertTrie("her");//插入成功返回true
std::cout << trie->InsertTrie("sh");//插入成功返回true
std::cout << std::endl;
auto acautomatic = new ACAutomatic(trie);//实例化AC自动机
std::cout<<acautomatic->SearchACTrie("yasherhs");//返回多模匹配到单词的个数
return 0;
}
输出:
11111
3