AC自动机 1
字符串好多算法都可以归到有限状态自动机里
然后5部分:
1.字符集
2.状态集合
3.初始状态
4.接受状态
5.转移
然后就开始看不懂了
AC自动机,通俗讲就是”Trie+KMP“
首先把所有模式串插到普通Trie树上,
这个建Trie在插入函数最后维护一个节点出现的次数
然后在上边构造失配指针
这个失配指针就是KMP引进过来的
像KMP那样构建失配指针!
KMP的原理:往回跳
这里也有一种近似往回的操作
不过还是有差异的:
对于节点u和父节点p和边(p,u)
1.存在(fail[p],fail[u])=(p,u),fail[u]即为所求
2.不存在,往回跳!嵌套式返回即可
3.一直到根节点,没找着也就这样了
不过这要求之前所有的fail指针必须求出
于是可以BFS跑一遍
有了fail指针,我们其实就可以理解为某个字符串(或者前缀)和其他字符串的后缀相连接了
不过,如果说一口气跳了\(inf\)次fail指针才跳到目标串呢?
这里就不得不优化一下下跳fail指针了
像并查集路径压缩一样:
直接把fail指针指向的节点改为自己的节点,就不用来回跳fail了
不过如果原来没有这个节点才这么干
有的话就不用这样了,就正常找fail指针就行l
于是我们有了一张Trie图
这个优点就是时间复杂度上的优化
维护额外信息?
考虑到Trie树自身是个数据结构这个事实
然后所有的问题在找fail指针建出AC自动机的时候就可以解决了
比方说一道例题:
AC自动机3:
给你一个文本串 \(S\) 和 \(n\) 个模式串 \(T_{1 \sim n}\),请你分别求出每个模式串 \(T_i\) 在 \(S\) 中出现的次数。
不过不保证互不相同
这个就挺板子的题:
首先对于重复串,使用一个数组去重
在节点维护一个结尾标记,表示以这个点结尾的第一个重复串
如果多次以这个点结尾,自然可以通过一个指向数组将之去重
然后就是正常的建AC自动机
最后统计一下答案就好了
每个点的标记和答案都是在Trie上的