Aho-Corasick Automaton · AC自动机
AC自动机是一个高效的字符串多模匹配算法,它的核心想法是把KMP的失配指针做到Trie上,从而实现对于所有模板字符串而言的单次文本串扫描出结果。由此可见,若要整下AC自动机,必须先掌握Trie和KMP。另外,AC自动机真的不是自动帮你AC的机子 其实你可以找AK自动机。
注意,下文涉及的Trie以及KMP主要为AC自动机做铺垫,详细算法介绍请见相应文章!
·字典树(Trie)
其实字典树、前缀树什么的是同一个东西。
不难看出,树上每个节点都对应了一个单词。Trie就是这样一颗多叉树(26叉较常用,图中省略了空子树),它的边用一个大小为26的数组记录,这样就可以实现O(1)向下查找。例如单词“tea”,它对应树中路径root (第零层) > 1-1(第一层左往右第一个) > 2-2 > 3-1 。
插入:从根开始,顺着边往下走(O(1)递进,实现见代码),当走到词的末位时标记当前节点
查询:从根开始,顺着边往下走(中途走不通视为查询失败),当走到词的末位时检查当前节点,确认是否被标记。
实现方法:市面上 有两种:数组模拟和充斥着指针的动态实现,建议初学者使用静态数组,dalao们请随意
代码一言不合就封装:
class Trie
{
private:
class Nodes
{
public:
Nodes* nxt[26];
bool end;
Nodes()
{
for(int i=0;i<26;++i)
nxt[i]=NULL;
end=false;
}
void init()
{
for(int i=0;i<26;++i)
nxt[i]=NULL;
end=false;
}
};
Nodes root,*now;
public:
Trie()
{
this->root.init();
this->now=NULL;
}
void init()
{
this->root.init();
this->now=NULL;
}
int insert(const char *s)
{
if(!s)
return 0;
this->now=&this->root;
for(int i=0;s[i];++i)
{
char ch=(s[i]>='a'&&s[i]<='z'?s[i]-'a':s[i]-'A');//假定大小写通用
//一般情况下这样写: char ch=s[i]-'a';
if(!this->now->nxt[ch])
{
if((this->now->nxt[ch]=new Nodes)==NULL)
{
return -1;//申请内存失败,返回错误信息
}
}
this->now=this->now->nxt[ch];
}
this->now->end=true;