作为一道蹂躏了我一天的题…我也没啥好说的了QAQ…
思路概述
我们知道,AC自动机是一种多模字符串匹配算法。构造 Trie 树 后,在模式串末尾一位的结点作上标记。平常的 AC自动机 是尽量能多接触到这些标记,使总值最大。本题倒是有点奇葩,要构造一个可行的无限长文本串,使没有任何子串为给出模式串中的一个。也就是说,我们需要让平常 AC自动机 的查询操作,尽量避免标记,能用失配指针跳转就跳转。
因为要有无限长的可行串,根据 AC自动机 的原理,我们可以将结点连接到儿子的边当作一条单向边,同时失配指针也当作一条单向边,如果存在一个环,且环上没有任何危险标记(即病毒代码段末尾一位的结点专门作的编号),此时 AC自动机 能一直在环上匹配,并且永远也不会得到为模式串的一个子串,就像程序中的死循环一样。这个找环我们可以通过 dfs 来实现。
注意事项
1 . 我们需要建立两个布尔数组,其中一个布尔数组记录每个节点在当前 dfs 走的路径上有没有被选中,另一个布尔数组记录每个节点历史上有没有被访问过。如果当前路径形成回路,就找到环了,应该还是比较好实现的。
2 . 避免危险标记,也就是说如果下一个结点拥有危险标记,就不走那个结点。
3 . 在构造失配指针时,一个很明显的优化是:如果一个结点拥有了失配指针,它指向的结点如果有危险标记,自己必然也危险,因为它到根结点形成的串是自己到根节点的后缀。
4.为什么要两个bool标记?因为如图1,如果不能保证当前dfs时在sta中的节点是链式结构的话,就会可能出现误判为环的可能。
5.为什么要在造指针的时候做优化,除了时间上的考虑似乎还有别的因素…如图吧。否则的话,如果只访问子节点,那永远成不了环。访问fail节点的话,又可能自环。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>