后缀自动机(学习笔记)

要看懂本文可能至少需要知道什么是后缀(

还得知道一些自动机的概念(

和ac自动机有个很大的不同,sam它的字符是放在边上的(似乎放在点上也可以),因为大部分图都是这样的,(比如下图),把点看成了能接受从起点到该点的路径串的状态。

T状态能接受abc和bc字符串。  ( ps:图随手画的)

首先定义一些概念:

endpos(t):表示子串t的结束位置。比如aabaa,endpos(“aa”)={2,5}

在sam中,每个endpos(t)即为一个状态,也就是说几个endpos(t)相同的不同子串共用一个状态;显然abab中,ab和b是共用一个状态的。

先定义一个状态:x

string(x):表示x状态能接受的所有字符串。

可得性质1

在string(x)中,短串是长串的后缀。

再定义几个东西:

string(x)_max,string(x)_min:分别其中的最长串、最短串。

又得性质2:

string(x)=={string(x)_min,string(x)_min+1,string(x)_min+2,,,,,,string(x)_max},就是说最长串的后缀连续出现在string(x)中,直到string(x)_min。

那么又会有疑问了,那长度小于string(x)_min的后缀呢?由于那些后缀的endpos(t)多了几个元素,便不和它们共用一个状态了。但还是有个东西把那些后缀也表示了出来。

定义Suffix指针:指向属于它后缀的状态。如下图绿色的线:

至此,,,似乎已经明白后缀自动机的结构了。

再补充两个点:

所有的子串即为从S出发的所有路径,即为所有状态的string(x)之和。

能轻易地知道每个子串的所有后缀。

状态总数为O(n)。

如何O(n)构造呢?

考虑递推,已经建好了前n-1个字符,再加入一个字符c,无非又多了n个子串。

定义pre[n-1]为前面n-1个字符构成的字符串。

这个时候我们只需看当时的末状态,即能接受pre[n-1]的那个状态,因为pre[n-1]的所有后缀加一个字符c就是新增的n-1个子串。

前面说过string(x)_max,string(x)_min,就不多说了,用一条边为c的边连接末状态和新状态,就解决了max-min+1个子串,也解决了pre[n]。

剩下可以分3类情况:

这个时候就需要借用末状态的Suffix指针了。

定义Suffix指针指向了y状态,上面说的新增状态为z。

1,y状态+c,在原有sam上不能发生转移,y新增一条边c指向z。

2,y状态+c,算了,太tm难了。

原文地址:https://www.cnblogs.com/lnu161403214/p/9801069.html