AC自动机的一些处理方法
一些经典的问题:
-
求各个模式串在文本串中出现了几次:
方法: 在AC自动机上先跑一边文本串,记录一下每个点被经过的次数,那么单词被经过的次数就是他的结尾在fail树中的子树的权值和
-
找是否有一个匹配不上匹配串的环:
方法: 把所有能匹配上匹配串的节点匹配记录下来,dfs从跟开始走所有我可以走的点,如果我走到了一个之前走过的点就说明出环了
实现: 一边调用fail一边更新当前点是否能走,因为fail记录的是前缀等于后缀,后缀深度一定比前缀大,所以fail一定在之前就走过了
-
求\(n\)个串总共出现\(m\)次的最短长度
方法: 求两两单词结尾之间的最短距离,初始矩阵\(a_{i,j}\)表示结尾\(i\)和结尾\(j\)之间不经过其他结尾的最短距离,矩阵快速幂\(m-1\)次
更新成\(a_{i,j}\)表示结尾\(i\)到结尾\(j\)之间经过其他\(m-2\)个结尾的最小距离,现在总共经过了\(m\)个单词结尾,再加上第一个单词的长度,就是最短长度,一共\(nm\)种情况取最小的一种
-
每次删掉文本串中的最早出现的模式串,剩下的字符继续拼在一起
方法: 跑AC自动机,模拟栈,发现匹配到一个单词,把属于这个单词的字符全部弹出,继续从之前匹配的位置继续往下找
注意问题:
- 在fail树上拓排的时候在更新fail指针的时候进行,如:
fail[trie[root][i]] = trie[fail[root]][i];
du[trie[fail[root]][i]] ++;
- 看当前字符是否能匹配上匹配串是在调用fail的时候进行,如:
trie[root][i] = trie[fail[root]][i];
vis[trie[root][i]] |= vis[trie[fail[root]][i]];