参考リンク:
https://blog.csdn.net/bestsort/article/details/82947639#commentBox
https://blog.csdn.net/niushuai666/article/details/7002823
ACオートマトンの説明:
まず、ACオートマトンの簡単な概要:アホ- Corasickの自動化、アルゴリズムはベル研究所、有名なマルチモードマッチングアルゴリズムの1年間の出力で1975年に生まれました。この一般的な例は、nワード(n個の単語、我々はパターン文字列それを呼び出す)あなたはどのように多くを見つけるために持っているので、期間がm個の文字が含まれています(私たちはマスター列それを呼び出す)、その後、物品を与える、によって与えられ、言葉は、記事に登場しました。ACオートマトンを知ってもらうために、トライトライKMPパターンマッチングアルゴリズムの基本を来て、持っています。KMPアルゴリズムはAC機が自動マルチモード文字列マッチングアルゴリズムであり、シングルモードの文字列マッチングアルゴリズムです。
ACオートマトンのコンストラクタ:
図1に示すように、n個のワードを有する第1の構成物トライ
図2は、ツリーのためのポインタの各ノードは、障害(表現することができないコードによって示される[]配列)、そしてこの障害ポインタKMPアルゴリズム幾分類似を提供しました。KMPアルゴリズムは、マッチが次の試合で配列内の次の位置へのジャンプを失敗した場合に時間を見つけるために使用されます。ポインタを合わせて、失敗はで次の位置にジャンプマッチが失敗した後、それは、この目的のためにあります
図3に示すように、主走査マッチした文字列
特定のACオートマトンのコンストラクタ:
まず我々はトライを確立する必要があります。しかし、ではない普通の木トライトライが、いくつかの特殊な性質を持ちます。
まず、三つの重要なポインタ、それぞれがあるだろう P 、 P->失敗 、 TEMP 。
1.ポインタpは、現在の文字の一致を指します。pは現在のマッチ文字列を表す、ルートを指している場合は空です。(ルートトライエントリは、実用的な意味ではありません)。
2.ポインタのp>失敗し、失敗のポインタp、P同じ文字ポイントノード、そうでない場合は、ポイントのルート。
3.ポインタの一時、テストのポインタ(自分の名前を付けるには、簡単に理解する!〜)、Pの文字と役割を失敗ポインタ、最大の効果スキャンするだけでなく、理解するのが最も難しいの確立に一致するノードを見つけること。
シーケンスs [1 ... M]に対応するトライ木ノードの。この場合、文字S [M]にP点が。別のノードへのミスマッチポインタジャンプによって次の文字、すなわち、P>次の[S [M + 1]] == NULL、で不一致が(P->失敗)した場合、ノードは、シーケンスSに対応します[I ... M]。あなたが不一致を継続する場合は、ジャンプシーケンスが続くシーケンスが空であるか、マッチが発生するまで。このプロセスでは、pの値は常に変化しているが、文字Pに対応するノードが変更されていません。このプロセスでは、私たちが見ることができますを観察し、得られた最終シーケンスは、最長の共通のサフィックスと比較しました。このシーケンスの最初のノードからルートにあるので、このシーケンスいくつかの接頭系列があるかもしれません。
ポインタが設定失敗:
まず、与えられたパターン文字列を"ash","shex","bcd","sha"
、我々はモードに基づいてトライの次の文字列を作成します。
:その後、我々は次のステップを知る
ポインタが試合の現在のポイントが失敗した場合、ポインタはそれほど戻っていない、失敗するポインタを置くために移動され、失敗する追加、ACオートマトン、ツリー上のタイヤに基づいて、ですが、道路にマッチします私たちは次はここを参照してくださいBCDでジャンプでC E、C」を見つけるために探していない見つけ、(現在のモードとサフィックス文字列パターン文字列が同じプレフィックス部分を失敗する指摘。行き、そのようなABCEのBCD次の文字(d)は、それがAを見つけられないで)
一般的には、BFSで構成されているのポインタ達成するために失敗し
、各モード最初の文字列(で、ルートノードの直接の子ノード)の最初の文字を必ずルートノードを指しています
今、第一層BFSが終わっ横断し、第二層始め
の第二層(ルートノードなどのレイヤ0)のa
サブノードがあるs
が、我々はまだしたいa-z
、我々は彼が(ルートノードに次のことを指してみましょう何の子ノードが存在しない場合、横断します図赤a
)
当我们遍历到s
的时候,由于它的父亲节点(即、a)的fail指针指向的节点(即、根)的直系子节点存在s
这个节点,我们就让他的fail指针指向他父亲节点(a
)的fail指针指向的那个节点(根
)的具有相同字母的子节点(第一层的s
),也就是这样(没错fail指针就是这样构造的
按照相同规律构建第二层后,到了第三层的h
点,还是按照上面的规则,我们找到h
的父亲节点(s
)fail指针指向的那个位置(第一层的s
)然后指向它所指向的相同字母根->s->h
的这个链的h
节点,如下图
完全构造好后的树
然后匹配就很简单了,这里以ashe为例
我们先用ash匹配,到h了发现:诶这里ash是一个完整的模式串,好的ans++,然后找下一个e,可是ash后面没字母了啊,我们就跳到hfail指针指向的那个h继续找,还是没有e?再跳,结果当前的h指向的是根节点,又从根节点找,然而还是没有找到e,程序END
扫描主串:
构造好Trie和失败指针后,我们就可以对主串进行扫描了。这个过程和KMP算法很类似,但是也有一定的区别,主要是因为AC自动机处理的是多串模式,需要防止遗漏某个单词,所以引入temp指针。
匹配过程分两种情况:(1)当前字符匹配,表示从当前节点沿着树边有一条路径可以到达目标字符,此时只需沿该路径走向下一个节点继续匹配即可,目标字符串指针移向下个字符继续匹配;(2)当前字符不匹配,则去当前节点失败指针所指向的字符继续匹配,匹配过程随着指针指向root结束。重复这2个过程中的任意一个,直到模式串走到结尾为止。
扫描代码:
1 int len=strlen(s); 2 int now=root; 3 int res=0; 4 for(int i=0;i<len;++i) 5 { 6 now=next[now][s[i]-'a']; //这个now就保证了我们找的肯定是这个s字符串中的一部分 7 int temp=now; //这就是一直找,找到了就加1,找不到就通过失败指针看其他地方能不能找到 8 while(temp!=root) //尝试这个节点的失败指针能不能找到模式串 9 { 10 res+=ends[temp]; //因为我们要找出来s串中包含几种模式串,所以要有赋值0操作 11 ends[temp]=0; 12 temp=fail[temp]; 13 } 14 } 15 return res;
![](https://img2018.cnblogs.com/i-beta/1633695/201912/1633695-20191212195053309-2021021648.png)
例题: