AC自动机的一些题目及思路

来源:互联网 发布:界面设计软件 编辑:程序博客网 时间:2024/06/05 08:45

先留着以后慢慢看



AC自动机 By strongoier

一、多模式串匹配

入门推荐http://www.cnblogs.com/Booble/archive/2010/12/05/1897121.html。

【例1】HDOJ 2222 Keywords Search

题目大意:

给出N(1 <= N <= 10000)个小写字母模式串(每个长度不超过50)和一个文本串(长度不超过1000000),求文本串包含了多少个模式串。

算法分析:

建立AC自动机后,注意匹配的过程中发现找到模式串时要把它的所有后缀(即从它的fail一直到根)也全计入答案,同时打上标记,以保证每个单词只统计一次。时间复杂度O(L)。

【例2】SPOJ JZPGYZ

题目大意:

给出N(1 <= N <= 10000)个文本串(总长不超过100000),Q(1 <= Q <= 60000)个模式串(总长不超过360000),问每个模式串在多少个文本串里出现过。

算法分析:

和上一题类似,建立AC自动机后,注意匹配的过程中发现找到模式串时要更新它的所有后缀(即从它的fail一直到根)的答案,同时打上标记,以保证每个单词只更新一次。注意char字符集为128,时间复杂度O(L)。

【例3】BZOJ 2754 喵星球上的点名

题目大意:

给出N(1 <= N <= 20000)个人的姓串和名串,有M(1 <= M <= 50000)次点名,只要点的串是某个人姓串或名串的子串那这个人就会被点到,问每次会点到多少个人和每个人会被点到几次。

算法分析:

本来直接使用AC自动机匹配就可以了,但由于串是10000以内的数字,如果直接暴力枚举10000复杂度是结点数*10000会爆时空。

于是我们想只要保存有用的就行了,用map可以很好的实现,遍历的话用iterator也没问题。时间复杂度O(LlogC)。

【例4】symbol 玄武密码

题目大意:

给出长度为N(1 <= N <= 10^7)的文本串和M(1 <= M <= 10^5)个模式串(每个长度不超过100),求每个模式串的前缀在文本串上的最长匹配长度。

算法分析:

将模式串建成AC自动机后,将文本串能匹配到的所有结点及其后缀都打上标记,最后在AC自动机上BFS一遍求出以每个结点结尾的路径与文本串匹配的最长前缀,最后只要输出每个模式串的结束结点的答案即可。时间复杂度O(L)。

二、运用Fail树

【例】BZOJ 2434 阿狸的打字机

题目大意:

给出一个打字机的操作序列(总长不超过10^5):在槽尾加一小写字母;删除最后一字母;槽不变,打印当前行。保证打印不超过N(1 <= N <= 10^5)次。给出M(1 <= M <= 10^5)个询问(x, y),查询第x次打印的串在第y次打印的串中出现了几次。

算法分析:

首先把这些串全部存起来是不现实的,那么我们想到使用Trie树来存储,并建立成AC自动机。

我们把fail指针全部反向形成的一棵树称为fail树。

考虑询问(x, y),实质就是在fail树上,找以x的末结点为根的子树上,有多少个结点在AC自动机中root -> y的末结点的路径上。

那么我们再次处理操作序列,就可以很方便地模拟出每一个root -> y末结点路径上的结点都标记为1的情形,下面我们需要处理对应的x的询问。

我们要做的是查询一棵子树的和,这是经典的DFS序 + 树状数组维护,在此不再赘述。那么,我们把所有与y对应的x答案都处理出来即可。

时间复杂度O(L + MlogL)。

三、结合动态规划

(一)全不包含

【例1】POJ 3691 DNA repair

题目大意:

给出N(1 <= N <= 50)个长度不超过20的致病DNA片段,给出一个长度为M(不超过1000)的DNA片段,要求修改最少的字符使得该DNA片段不包含任何致病DNA片段。

算法分析:

我们首先将致病DNA片段建立AC自动机,并计算转移函数。

令f[i][j]表示串中到第i位,AC自动机中到第j个结点的最小答案,那么可以转移到f[i + 1][nxt[j][k]]。

注意以nxt[j][k]结尾的路径不能包含致病DNA片段,这在计算fail指针的过程中应顺带传递。

时间复杂度O(ML)。

【例2】POJ 1625 Censored!

题目大意:

给出大小为N(1 <= N <= 50)的字符集,P(0 <= P <= 10)个长度不超过10的非法单词,求长为M(1 <= M <= 50)的合法句子个数。

算法分析:

和上一题类似地,令f[i][j]表示串中到第i位,AC自动机中到第j个结点的方案数,需要使用高精度。

需要注意的是,这题有字符在128~255之间,要用unsigned char。

时间复杂度O(NML)。

【例3】POJ 2778 DNA Sequence

题目大意:

给出M(0 <= M <= 10)个长度不超过10的致病DNA片段,求长度为N(1 <= N <= 2 * 10^9)的不包含任何致病DNA片段的DNA片段个数。

算法分析:

和上一题类似地,令f[i][j]表示串中到第i位,AC自动机中到第j个结点的方案数。

由于N很大,我们可以把转移看成是在一个有向图上求从A到B经过K条边的路径数。

这就是一个经典的矩阵乘法问题了,利用邻接矩阵自乘K次,快速幂搞定。

时间复杂度O(L^3logN)。

(二)至少包含一个

【例1】BZOJ 1030 文本生成器

题目大意:

给出N(1 <= N <= 60)个长度不超过100的单词,求长度为M(1 <= M <= 100)的至少包含一个单词的文本的个数。

算法分析:

我们首先将单词建立AC自动机,并计算转移函数。

令f[0/1][i][j]表示是否包含过单词,到文本第i位,AC自动机上第j个结点时的方案数,递推是显然的。

时间复杂度O(ML)。

【例2】SPOJ GEN

题目大意:

给出N(1 <= N <= 10)个长度不超过6的单词,求长度为M(1 <= M <= 1000000)的至少包含一个单词的文本的个数。

算法分析:

假如和上一题类似地,令f[0/1][i][j]表示是否包含过单词,到文本第i位,AC自动机上第j个结点时的方案数,状态有120个,在此题中会超时。

考虑加一个终止点表示已经包含了单词,那么凡是nxt指向单词结尾的我们把这个转移到终止点上,而终止点到终止点的转移就是26种,因为只要包含过一个单词那么以后就随意了。

这样状态数只有60个,时间复杂度O(L^3logM)。

(三)全包含

【例1】BZOJ 1195 最短母串

题目大意:

给出N(1 <= N <= 12)个长度不超过50的字符串,求最短且字典序最小的字符串,使得之前N个字符串都是它的子串。

算法分析:

由于要全包含,我们考虑使用状态压缩动态规划。

令f[i][j]表示到AC自动机上第i个结点,包含子串的状态为j的最小串。

为了保证每个状态只被转移一次且必须为最小串,我们人为地规定转移顺序,即从空出发,每次从小到大往后加一个字符,更新到达的状态。

那么这个过程用BFS是很容易实现的,时间复杂度O(L * 2^N)。

【例2】BZOJ 1559 密码

题目大意:

给出密码长度M(1 <= M <= 25)和已经观察到的N(1 <= N <= 10)个长度不超过10的子串,求满足观察要求的密码个数,如小于等于42个则按字典序输出所有解。

算法分析:

令f[i][j][k]表示到密码第i位,AC自动机上第j个结点,包含子串的状态为k的方案数,转移是显然的。

统计出总数以后,如发现小于等于42,则我们需要再倒着将DP的转移关系全部记录下来,过程比较繁琐,细节较多。

最后将所有的字符串排序即可。时间复杂度O(ML * 2^N)。