您的位置:首页 > 其它

后缀自动机_SAM学习大记

2017-12-21 12:27 148 查看
刷新了我对难懂算法的定义,下面都是自己的一些粗浅理解

参考资料:后缀自动机详解 http://blog.csdn.net/qq_35649707/article/details/66473069

陈立杰冬令营讲稿 https://wenku.baidu.com/view/90f22eec551810a6f4248606.html

定义

1) 可以接收串S的所有后缀的最简状态自动机

2) 是一张dag,其中点被称为状态,边被称为转移,每个转移都有一个标号(字母集中的一个元素) ,某一状态t_0被称作初始状态,由它能够到达其余所有状态。

3) 从一个状态出发不能有两个相同标号的转移

4) 一个或多个状态被标记为终止状态。如果我们从初始状态t_0经由任意路径走到某一终止状态,并顺序写出所有经过边的标号,你得到的字符串必然是s的某一后缀。 也就是说s的所有子串都能被这样类似地表达出来,而且内容相同的子串的表达路径是同一条.

基本认知 (若干定理) 必看

较多感性理解,如不适则请移步相关论文,课件.

定义: endpos(v)表示字符串v在s中出现终点(右端点)集合. endpos(t0)=全集

1) 具有相同endpos集合的子串被称为在同一个等价类中(动机: 可以转移到的后缀相同)

2)一个状态,即一个点,表示的是某个endpos集合(某等价类)。一个子串属于该等价类,当且仅当这个子串是t0到该点的一条路径.

重要: 3)两个合法endpos集合要么是包含关系,要么不相干. 易反证.

4)某一终点等价类中的字符串互为后缀,它们的长度依次取区间[x,y]内的所有数。

证明: 若长度为x与y的都属于该等价类,那么长度处于他们之间的也显然属于它.

5)若状态x有转移(x,t),则可以判断:该endpos集合中的某些位置后面有一个c,从而可以加一个字符,转移到一个新的endpos. 并不是所有位置后面都有c。

反过来,若endpos中某些位置后面有c,则这个状态一定有一个标号为c的转移.

(建议与定义4最后一句话一起理解)

看懂上面的之后,换句话说:一个等价类有(x,t)转移,不意味着在其中的所有字符串都可以加c,而是一部分可以加c.

后缀自动机可以被看作:

s的所有子串可以按照它们的终点集合被分成等价类。

后缀自动机由一个初2始状态t_0和所有不同的终点等价类所对应的状态以及他们之间的转移组成。

定义:link(x)表示对于某个endpos集合x(状态x),包含他并且endpos集合大小最小的状态. (就是比他稍微大一点点的那个endpos集合)

link链:顺着link一直走直到t0,所到达的点集.

link链组成了一棵以t_0为根的树。

证明.考虑任意状态v≠t_0.后缀链接link(v)指向的状态所对应的字符串长度严格小于它本身(根据定义)

因此,沿着后缀链接移动,我们将早晚到达t_0,它对应一个空串。

6)若等价类x中的字符串长度区间为[x,y] (见4),link(x)中的字符串长度集合应为[z,x-1]

解释:就相当于随着字符串长度不断减小(要求不断放宽),endpos一点点变大.

7)顺着link(x)走实际上就是在扩展endpos集合. (串的要求逐渐放宽)

8)x的link链 就是 所有包含了x的endpos集.(endpos一点点变大,直到全集,又综合3可感性得出此结论)

推论: 若等价类x包含了等价类y,那么y 一定 在x的link链上 。

定义:对于一个等价类x,len(x)是在他之中的最长字符串长度.

线性构造算法(顺序看下去)

对每一状态,需要维护两个基本值:link与len.

对于串s我们有构造好的自动机,并且全串s所对应的状态是last.

(显然last的endpos集合只有一个元素:s的长度,因此)

1) 考虑新插入一个字符c. 必须要新建一个状态cur,endpos={s+c末尾位置},

因此len(cur)=len(last)+1=s+c长度

2) 因为s的后缀所在的endpos全在last的link链上(因为他们全部包括s的末尾位置),需要为他们添加转移c以接收新串的所有后缀

因此从last开始顺着last的link链往上走,当前点x若没有转移c则为他添加一条(x,cur)标号为c的转移,

走到t0 或 遇到一个点有标号为c的转移则停止.

若走完,仍没有任何一个点有标号为c的转移(t0也没有),则将link(cur)设为t0

(因为t0也没有c的转移,显然没有包含cur的endpos了),结束过程

3) 设遇到第一个有转移c的点为x,并且这条转移是(x,t);首先len(t)>len(x),因为根据转移,len(t)至少等于len(x)+1

因为不能为他直接添加新的转移,我们考虑可不可以为点c的endpos集合添加s+c这个位置,并在link与len中做出对应更新呢?(其实endpos集合都是假想的,实际上并不需要对于每个点保存)



红色框框为在此endpos中在最长的串,由于(x,t),因此t的endpos集合应该比x往后挪一位,并且更小.

重要:

我们现在给t添加s+1这个位置,会发现po这一段不一定和前面两个endpos中的元素中对应位置匹配。

有一种情况可以使得po长度为0,也就是len(t)=len(x)+1



当这种情况时,我们假想为t添加s+1,并可以直接将link(cur)赋值为t,并结束过程。

将link(cur)设为t之后相当于为t的link链上的endpos都添加s+1(这关系到endpos集合的求法,见参考文章的应用部分),并且t是endpos大小最小的,因此link(cur)指向t.

还有一个疑问:为什么能结束过程,而不用继续为后面的点添加转移?见性质5,后面的点都是x代表的串的后缀,所以它们加一个c得到的一定是t所代表的串的后缀,因此在t的link链上

现在来解决一般情况:len(t)>len(x)+1

原因: 在等价类x中,长度不超过len(t)+1的串的endpos集合会多s+1这个元素,而其他不会。 因此该等价类分裂为两个等价类

新建点nt ,根据原因,len(nt)=len(x)+1;

除此之外,t的所有数据全部复制给nt.然后再调整link,nt的endpos集合是endpos(t)+(s+1),正好比endpos(t)大一点点,所以link(t)=nt(nt的endpos集合合法,因此由定理3得这样连没有问题)

将x的link链(包括x)上连到t的转移全部改成连到nt上。 (如果遇到c的转移一个不是t的就不用继续改下去,因为它转移的在nt的link链上)

最后link(cur)=nt.

无论何时退出,last都应该更新为cur.

复杂度分析

2018/1/5 UPD::

抛开构造不看,因为点数不超过2len.

所以几乎所有操作复杂度都是O(n)的。

问题就是要证明构造也是O(n)的。

应用部分

1.求endpos集合大小

题目: 4072. 【TJOI2015】弦论(string)

size(endpos) 就是link链组成的树中子树的大小.

也可理解成加点时(拆点时不计),对当前link链上所有点+1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: