$AC$自动机

  • 直到现在才发现我对\(AC\)自动机还是一知半解(爆论
  • 前期储备知识:
    • 字典树 \(trie\)
    • 单模式串匹配 \(KMP\)
  • \(AC\)自动机看起来很高大上(仿佛考场上每道题写AC​自动机都能过)。但其实上\(AC\)自动机不过是将\(KMP\)套在\(trie\)上(形象化来讲,即具体实现是利用这两种算法的思想,实际上从定义出发并不相同)
  • 首先,当我们手头有\(N\)个模式串时,我们仿照\(KMP\)的思想,对于第\(x\)个模式串的任意一个位置\(pos\),找到最大的在这\(N\)个模式串的前缀\([start:]\)中出现过的后缀\([:pos]\),即\([start_s:]^{s}=[:pos]^{s_x}\)
  • 但往下深入思考的时候,我们将遇到一个棘手的问题
    • \[如果那个最长的前缀同时出现在多个模式串中怎么办?\\这样我们并不知道应该从哪个模式串的前缀末尾开始匹配\]
  • 那怎么办呢?于是我们自然而然就能想到 用\(trie\)把这些相同的前缀合并起来,这样就解决了我们之前遇到的问题。
  • 接下来,问题少年又开始提问题了。那你每次插入一个模式串都需要把前面的\(fail\)指针重建(因为会出现更长的前缀匹配前面的后缀),复杂度不是爆炸了吗?所以我们必须在所有模式串插入完成后在一起建\(fail\)指针,因此\(AC\)自动机从理论上讲是一个静态数据结构
  • 既然\(fail\)指针的构建是同时对\(N\)个模式串,因此构建方式与\(KMP\)\(fail\)指针有些许不同,在这里简略讲讲...
    • 我们先在脑中构建一张包含你喜欢的字符串的\(trie\)树(建议拿张草稿纸画画)
    • 回忆\(KMP\)构建\(fail\)数组的方式,我们按照顺序利用前面已得到的\(fail\)指针辅助构建。类似的,我们按照\(trie\)树的深度一层一层构建\(fail\)数组。
    • 当要计算\(u\)\(fail_u\)的时候我们找到\(fail_{fa_u}\)看它是否有对应\(fa_u\to^cu\)\(c\)字符,如果没有我们像\(KMP\)一样,暴跳\(fail\)指针,直到找到\(c\)字符对应出边
    • 这里还有一种更简便的构建\(fail\)指针的方式(实际上只是常数优化,思路是一样的)。具体来说,你对每个节点\(u\)\(26\)条出边(对于文本只存在小写字母来说)无论出边有没有对应节点,都构建\(fail\)指针,这样在求节点\(v\)\(fail\)指针的时候,我们只要把\(fail_{fa_v}\to^c x\)\(x\)继承过来即可。

猜你喜欢

转载自www.cnblogs.com/shjrd-dlb/p/10964566.html
今日推荐