递归实现双数组字典树AC⾃动机
普通AC⾃动机的递归实现过程
void build_ac(Node *node) {
/* 根节点的⽗亲 == NULL */
if (node == NULL) return ;//递归出⼝:当node为空,返回.
/* 因为该递归思想是深度优先搜索进⾏ac⾃动机的创建,且ac⾃
动机的失败指针在建⽴的过程中必须得先将⽗亲节点的失败指针确
定好后才可建⽴孩⼦节点的失败指针,所以如果当前节点的失败指
针为空时,向上返回到其的⽗亲节点,看看该⽗亲节点的失败指针
是否创建完毕,若没建⽴完毕,向上返回到⽗亲节点的⽗亲节点
(该过程向上递归) */
if (node->fail == NULL) {
build_ac(node->father);
}
for (int i = 0; i < BASE; i++) {
//该⾏确定当前⼦节点的失败指针
if (node->next[i] == NULL) continue;
/*若他的孩⼦ = NULL,跳过*/
if (node->next[i]->fail) continue;
/* 若他的孩⼦有失败指针,跳过。跳过是为了避免失
败指针的重复建⽴ */
Node *p = node->fail;/*p节点有可能等于根节
点,所以最上⾯的root有可能== NULL*/
Node *pre_p = node;//⽤来保存上⼀次的值
while (p && p->next[i] == NULL) {
/* 如果node的失败指针没有失败指针则向上递归
到该节点的⽗亲节点,过程同上,向上递归最后的
结果是node的失败指针所在的整条⽀路的节点的
失败指针都建⽴完毕 */
if (p->fail == NULL) build_ac(p->father);
/* 否则跳到失败指针所指的节点 */
p = p->fail;
}
if (p == NULL) p = pre_p;/*若失败指针指向
空,则将上⼀次node赋值给p*/
else p = p->next[i];/*表⽰在p的孩⼦节点中找
到了想要找的节点,将p的孩⼦节点赋值给p*/
node->next[i]->fail = p;/*所以node孩⼦的失
败指针为新的p节点*/
build_ac(node->next[i]);//继续递归node的孩⼦节点
}
}
双数组字典数的递归建⽴过程
/* 该函数是⽤来确定now的孩⼦是否真的是它⾃⼰的孩⼦ */
int has_child(DATNode *trie, int now, int i) {
return abs(trie[trie[now].base + i].check) == now;
}
void build_ac(DATNode *trie, int now) {
if (!trie[now].check) return ;/*如果now的check
值为0,表⽰他没有⽗亲节点,即节点本⾝ == NULL,返回*/
if (!trie[now].fail) build_ac(trie, abs(trie[now].check));
/*向上递归返回⽗节点的过程同上*/
for (int i = 0; i < BASE; i++) {
if (trie[now].base + i == 0) continue;
/* 若他的孩⼦不是他⾃⼰的,跳过。因为会出现他的
孩⼦的check值存的不是他⾃⼰的情况 */
if (!has_child(trie, now, i)) continue;
if (trie[trie[now].base + i].fail) continue;
int p = trie[now].fail;
/*看now失败指针的孩⼦中有没有这个孩⼦,没有就跳
到失败指针的失败指针上继续找*/
while (p && !has_child(trie, p, i)) {
if (p->fail == NULL) build_ac(p->father);
p = trie[p].fail;
}
if (p == 0) p = 1;/*p = 0代表失败指针为空,1
代表根的base值,当now的失败指针为空时,now的失
败指针指向根节点*/
else p = trie[p].base + i;/*now孩⼦的失败指
针就是p的孩⼦之⼀*/
trie[trie[now].base + i].fail = p;
/*所以now的孩⼦的失败指针指向刚找到的节点*/
build_ac(trie, trie[now].base + i);
/*递归建⽴孩⼦节点的失败指针*/
}
}