AC 自动机入门
发现了一种 AC 自动机的科学讲法,能够更准确地描述自动机中的数学模型和代码中的对象之间的关系。
字典树模型
假设有个字符串,字符集为,这些字符串构成的字典树为。其中表示字典树的所有状态,即所有的结点,而表示字典树的转移函数,即转移边。
考虑到任意都唯一对应一个字符串,我们也认为是一个字符串集合。
AC 自动机模型
对构建的 AC 自动机可以表示为。
任意,与唯一对应,定义为中所有的后缀(包括)形成的字符串集合。也就是说是一个字符串集簇。不妨记这个双射为,。
是 AC 自动机的转移函数。对于,,假设(且是的后缀)。若存在最大的使得存在,那么(否则不定义)。
要求出,我们需要一个辅助函数。对于,。这也是所谓的失配指针。有了,我们就可以观察到。
引理 1:设(),那么。
证明:不是所有的都存在。
断言 1:,若存在,那么。
证明:由引理 1,可以发现末尾去掉得到的集合。结合的定义,在中最大的使得存在,一定也是中最大的,那么一定有。
断言 2::
证明:由定义和定义。
根据上述断言可以得出 AC 自动机的代码实现:
struct AC {
int tr[SZ][ALP], fail[SZ], tot;
void init() { tot = -1, new_node(); }
int new_node() { return ++tot, fail[tot] = 0, memset(tr[tot], 0, sizeof(tr[tot])), tot; }
void insert(const char *s) {
for(int u = 0; *s; ++s){
int c = *s - 'a';
if (!tr[u][c]) tr[u][c] = new_node();
u = tr[u][c];
}
}
int q[SZ], ql, qr; // q里就是BFS序
void build() {
ql = 1, qr = 0;
FOR(i, 0, ALP - 1) if (tr[0][i]) q[++qr] = tr[0][i];
while (ql <= qr) {
int u = q[ql++];
FOR(c, 0, ALP - 1) {
if (tr[u][c]) fail[tr[u][c]] = tr[fail[u]][c], q[++qr] = tr[u][c];
else tr[u][c] = tr[fail[u]][c];
}
}
}
};
修订记录
- 2023年5月4日 第2次修订
- 2020年12月28日 创建文章