后缀自动机复习笔记

首先发现原来的理解好乱啊...这次换一种理解的方式吧...

设原串为一个长度为$n$的字符串$T$

我们考虑这个字符串的每一个子串$S$,用一个集合$endpos_{S}$代表$S$在$T$中出现的所有右端点的集合

例:$T="aaabc"$,$S="aaab"$,则$endpos_{S}=${$4$}(下标从1开始是个比较好的主意qwq)

那么考虑另一个问题:这个串的所有子串的$endpos$两两不同吗?

显然不是的,因为显然$S="aab",S="ab",S="b"$时均有$endpos_{S}=${$4$}

那么我们称这些东西构成了$endpos$的一个等价类,用$len$来记录这个等价类中长度最长的字符串的长度,在这个例子中,显然最长的字符串是$"aaab"$,因此$len=4$

接下来考虑几个性质:

如果我们在一个子串之前加入一个字符$c$得到一个新的子串,那么这两个子串的$endpos$之间会有什么关系呢?

很显然,$endpos_{'c'+S}\subseteq endpos_{S}$!

这是比较显然的,因为如果$'c'+S$在位置$p$出现过,那么$S$一定在位置$p$出现过

那么基于这些包含关系,我们事实上可以把这些不同的$endpos$等价类引出父子关系!

举个例子:

$T="acbc"$

列个表格:

  a b c ac bc acb cbc cb acbc
endpos 1 3 2,4 2 4 3 4 3 4

那么我们把这些等价类引出这样的关系,立刻得到:

这是一个树形结构!

父节点一定是子节点的后缀!

我们已经知道后缀自动机了,因此你或许能看出这就是parent树!

当然,我们把顺序反过来了...先出了parent树

这玩意有啥用?

可能没啥用,但是...我们基于这一点能更好的理解后缀自动机!

接下来考虑后缀自动机:

在这个基础上来理解后缀自动机,就比较容易了:后缀自动机其实是一个有向无环图,他能够识别这个字符串的所有子串,而不是这个字符串字串的串则一定不能被识别!

同时,后缀自动机需要维护一些指针(注意不是有向无环图的边),使得按照这些指针能形成上面那样的树形结构,而这个树形结构从某种意义上将才是后缀自动机的核心!

这里也就是说,刚才那棵树上的节点就是后缀自动机上的所有节点!

因此我们在构建后缀自动机的时候,不妨考虑一直维护这棵树的性质,理解起来可能会容易一些:

从一个简单的例子入手:

例:$T="acc"$

首先我们插入第一个节点$a$:

这个好理解,蓝色的是我们维护的指针,黑色的是DAG的边

再插入第二个节点$c$,得到:

这里开始出现几个小问题:

第一个:出边怎么连?

直觉告诉我们:应该直接顺着上一个节点连出来

可是首先我们知道,后缀自动机应当能够接受这个字符串的所有子串,可这样根本没有办法接受子串$"c"$嘛!

那么这么看来,我们其实应该补上一些出边

怎么补呢?

我们知道,加入一个新的节点以后,产生的新的无法被识别的字符串一定是新串的一个后缀!

因此如果需要补充出边,我们一定要补全识别这些后缀

而新串的一个后缀去掉这个新的节点以后得到的是什么?

原串的后缀!

如何找到原串的后缀呢?

直接顺着指针往上跳嘛!

对于跳到的节点,补上新节点这个出边就好了嘛!

回到这个例子,我们补全之后的后缀自动机应该长这个样子:

接下来,我们考虑维护新加入的节点的指针

显然没有等价类的关系,直接指回根节点即可

就是这样:

接下来,加入下一个节点:

好像很友好

但还是上面两个问题:

怎么维护出边?

顺着指针往上跳?

可是...这次跳到的点已经有这个出边了!

那怎么办?

 有点麻烦了...

更麻烦的还在下面!

我们标记一下这几个点:

然后分析一下$len$的关系:

发现$l_{p}>l_{f}+1$!

这说明什么?

这说明$p$等价类中的一些串是不能通过单纯在$f$等价类后面添加一个字母得到的!

那么就产生了一个问题:当加入一个新节点$n$以后,节点$p$产生了信息的丢失!

为什么?

原来,$endpos_{p}=${$2$},他维护了两个子串:$"ac"$和$"c"$,而$l_{p}=2$(来源于子串$"ac"$),也就是说我们实际上藏起来了$"c"$这个串

但是,当我们再次引入$"c"$这个节点时,子串$"ac"$的$endpos$没有变,然而子串$"c"$的$endpos$却发生了改变,这样的话把$"ac"$和$"c"$放在一起是不合法的!

因此我们需要重新构造了

我们把这个压缩节点展开,新建一个节点,就是:

(由于分裂了这个节点,我们应该把$p$的所有信息继承给$np$,只修改$len$即可)

同时发现$p$的指针需要修改了,因为他的$endpos$是$np$的$endpos$的一个子集,因此改一下指针,就得到:

好像还差个新节点的指针没处理啊...

直接指向新建的节点就可以了,因为此时满足$len_{np}=len_{f}+1$

等等,为什么这样就可以直接建立呢?

考虑$len_{np}-len_{f}=1$,也就是说明$np$这个节点上只维护了一个字符串,这个字符串没有压缩信息,而且加入了新的第$i$个字符以后这个节点的$endpos$集合同时增加了一个$i$,那么仍然保证$endpos$集合的正确性

所以在一开始处理的时候,如果本来就能满足这个条件,也就不需要分裂节点了

所以完整的后缀自动机就长成了这个样子:

如果我们把蓝色的线拎出来,就能看到一棵树形结构了

同时我们发现,父子节点之间的$len$的差值就是子节点自己这个$endpos$等价类中串的个数(很显然,一个等价类中的串长连续)

这样后缀自动机和parent树就都搞出来了

posted @ 2019-07-03 18:39  lleozhang  Views(168)  Comments(0Edit  收藏  举报
levels of contents