我个人的理解 若有不当还请斧正
Tire(后缀数组) -> AC自动机 -> Tire图
以上是一个拓扑图的形式 先理解tire树再来搞懂 AC自动机
tire就不说了,直接来AC自动机。
AC自动机:
基础 Tire + KMP
用途:可以直接求一组模板串中有多少个模板串在主串中出现过。
怎么求?
先回忆一下KMP的匹配过程,就是预先处理出一个next数组来存储如果当前的模板串的字符与主串的字符不匹配时,
我们通过利用前面已经匹配的信息得到模板串可以往后直接移动最大多少位重新匹配而没有冗余。
我们预处理next数组的时候用到的是上一个字符的next
for(int i = 2; i <= n; i ++)
{
int j = ne[i - 1]; // 每次用到上一个字符的next
while(j && p[i] != p[j] + 1)j = ne[j];
if(p[i] == p[j + 1])j ++;
next[i] = j;
}
利用这种思想我们带入到tire 中假如我们已经构建了一个tire树,需要把树上的每一个节点的next都算出来
一维的时候我们next 根据前一个字符的next 计算 那么二维的话每个节点的next就需要父节点维的时候我们next 根据前一个字符的next 计算 那么二维的话每个节点的next就需要父节点next进行计算,意会一下。
先枚举当前节点可能存在的所以儿子(如果是都是小写字母的话就是26个 如果为01串就是2个 看情况而定)
1) 儿子不存在 直接pass
2) 儿子存在
假如我们前i层的next 已经算好了,第i + 1 层存在一个字符为’x’的节点,那么它的next数组等于它父节点的next
数组对应的那个节点的儿子为’x’的指针,如果这个指针不存在就继续往前找,知道找到或者到根节点啊结束。
while(j && !tr[j][i]) j = ne[j]; // j代表的是这个节点的next的指针 tr[j][i] 代表j指向的这个地址的儿子为i的字符是否存在
整个过程一层层来求用bfs 最终把所以节点的next全部存下来 over.
下一把如何计算当前节点是否出过
依次枚举主串 与j指针的儿子进行比较如果这个儿子存在那么吧j指向这个儿子
反正j就回溯到next指针的位置。
以上就是AC自动机的建立过程
tire图就是对AC自动机的一个小优化
for(int i = 0; i < 26; i ++)
{
int p = tr[t][i];
int j = ne[t];
if(p)
{
while(j && !tr[j][i]) j = ne[j];
}
if(tr[j][i])j = tr[j][i];
}
这个while循环里面存在一个问题就是如果next指针指到的位置的儿子不存在这个字符那么就一直往回找直到找到为止
我们想一个办法可不可以直接让这个指针直到它要指的位置 当然可以了
如上图假如它的儿子不存在那么我们就将它指向它父节点的儿子指针 ,假如他父节点的儿子指针也不存在那么就他父节点的儿子指针就会指向它的父节点的儿子指针
假如已经到了第i层那么前面的已经更新好了,如果他的儿子不存在直接把它指向它的父节点指向的地方的儿子的位置即可。假如存在儿子那么直接指向它父节点的儿子等于这个节点的位置即可,因为前面已经更新好了。
有点绕 第一次的接触的同学多多思考一下画个图就明白了,我也是花了一下午才明白的。
大家要有耐心呀`。
for(int i = 0; i < 26; i ++)
{
int &p = tr[t][i];
int j = ne[t];
if(!p)p = tr[j][i];
else
{
ne[p] = tr[j][i];
}
}
[AC自动机超级详细](https://www.cnblogs.com/hyfhaha/p/10802604.html)
内个 最上面回忆一下KMP的时候 有一行写成了 p[i] != p[j] + 1 ,应该是p[j+1]
原来AC自动机就是KMP+TRIE啊!!!tqltql
图片不会插。。
有一个上传图片,推荐大佬以后写一个AC自动机的分享!
上传图片咋用的
本地上传啊 有个图标的
已经找到
但是图片找不到了,以后有空再写了吧。