正好写这个博客和个人某个别的需求重合了。。。我就来说一讲SAM啦qwqhtml
后缀自动机,也就是SAM,是一种极其有用的处理字符串的数据结构,能够用于处理几乎任何有关于子串的问题,但以学起来异常困难著称(在机房里,最早学会SAM的永远是大佬(好比litble和zyf(他在退役前就学了)))。算法
可是!!!当你学了SAM并熟练地刷了几道题后,你会发现——你以前为了学SAM而强行理解的许多定理,对你应用SAM一点用处也没有!为了引出构造算法,几乎全部博客都会详细地解释“你为啥要这样作”,然鹅。。。数组
<font face="黑体" color="#ff00ff" size=5> SAM彻底能够当成黑盒来用!!!!!! </font>数据结构
因此我打算写一篇SAM速成博客。。。保证即学即会!学习
<font face="黑体" color="#00a0ff" size=5>Warning:想完全理解后缀自动机吗?那你有好消息了!请当即关闭此页面,在百度里搜索“后缀自动机 陈立杰”,开始愉快的学习吧!</font>(讲真,陈老师的ppt是讲的最好的,别的博客无能出其右者)spa
SAM是一个DAG(有向无环图),每一个点表明一个**"状态"**,边表明状态转移,边上有一个字母。SAM有一个起始状态(称为起点),从起点开始,沿着边不断走下去,就能够获得一个字符串。记当前停留节点为$x$,走出来的字符串为$S$,称节点$x$可表明字符串$S$。记$x$可表明的串中长度最长的串的长度为$len(x)$。指针
另外,除起点外的每一个节点还拥有一个**“后缀连接“**,记做$fa(x)$。后缀连接组成了一棵树,别的性质在构建完以后再讲。code
存储SAM利用的是相似于Trie树的存储结构,即便用$ch[][26]$数组保存状态转移的边。htm
知道了这些,构建SAM的工做就能够开始了。blog
准备工做:创建数组$ch,fa,len$,准备指针$last,cnt$。SAM的构造方法是不断地向已经建好的SAM中加入新的节点。$last$表示上一个被插入的节点,$cnt$表示SAM中的节点数量。一开始,$last=cnt=1$,表示只有一个起点的初始SAM。
接下来,假设要往SAM里加入一个字符$x$。
将你的字符串的全部字符都一一进行如上操做后,你就获得了用你的字符串构建出来的SAM。
你不须要知道为何这么操做能够获得SAM,你只须要记下如下的代码,作几道题强化记忆,而后就能够用SAM的性质来秒题了。
void insert(int x) { int np=++cnt,p=last; len[np]=len[p]+1,last=np; while(p&&!ch[p][x])ch[p][x]=np,p=fa[p]; if(!p)fa[np]=1; else { int q=ch[p][x]; if(len[q]==len[p]+1)fa[np]=q; else { int nq=++cnt;len[nq]=len[p]+1; memmove(ch[nq],ch[q],sizeof(ch[nq])); fa[nq]=fa[q],fa[np]=fa[q]=nq; while(ch[p][x]==q)ch[p][x]=nq,p=fa[p]; } } }
如今,你已经拥有SAM了,你须要知道它有什么用。这里列举了SAM的一些基本且经常使用的性质。
<font size=5 color=#7777ff>请牢记如下每一条内容!都十分有用!不要去问“为何是这样的”!(若是必定要问,请参照上文蓝色放大的Warning)</font>
首先,SAM的点数与边数都是$O(n)$的。<font color=#00cc00>记住,因为每次插入最多新建两个点,因此应该开字符总量两倍的空间。</font>计算空间时别忘了你开了26倍的$ch$数组。
在SAM上从起点开始沿着边随便走走,获得的必定是子串。同时,每个子串均可以在SAM上走出一条惟一对应的路径。也就是说,子串和SAM上从起点开始的每一条路径一一对应。路径数等于子串数。
起点能够看作是表明空串的点。
重点:定义子串的$right$集合:这个子串在原串中全部出现的位置的右端点的集合。
好比说:<font color=#00aaff>AA</font><font color=#ff0000>AAB</font><font color=#00aaff>BAAA</font><font color=#ff0000>AAB</font><font color=#00aaFF>A</font><font color=#ff0000>AAB</font><font color=#00aaff>BAA</font>
子串<font color=#ff0000>AAB</font>出现了3次,右端点集合为${5,12,16}$。这就是子串<font color=#ff0000>AAB</font>的$right$集合。
一个节点可以表明的全部子串的$right$集合是同样的。$right$集合相等的子串必定被同一个节点表明。(因此,咱们会使用“节点的$right$集合”这个说法。)两个节点的$right$集合之间要么真包含,要么没有交集。若节点$y$的$right$集合包含了节点$x$的$right$集合,那么$y$能表明的子串均为$x$能表明的子串的真后缀。
重点:定义节点$x$的后缀连接$fa(x)$:若是有一些节点的$right$集合包含了$x$的$right$集合,$fa(x)$是其中$right$集合的大小最小的那一个。
后缀连接们组成了一棵“后缀连接树”(不是后缀树)。后缀连接树的根为起点。若节点$y$的$right$集合包含了节点$x$的$right$集合,那么$y$在后缀连接树上是$x$的祖先。
一个节点的$right$集合等于他在后缀连接树上的全部儿子的$right$集合的并集。并且儿子的$right$集合之间两两没有交集。
每一个节点能表明的子串的长度范围是一段连续的区间。这很好理解,由于它们的结束位置都是相同的。
咱们求出每一个节点能表明的最长串的长度(即$len(x)$)了,那最短长度呢?其实就等于后缀父亲节点的$len+1$。也就是说,全部本质不一样的子串的数量等于$\sum len(x)-len(fa(x))$。
以上就是SAM的基本性质~对于一道特定的题,你可能须要经过上面的性质推出你须要的新性质。若是你还有什么疑问能够向我留言,我(在退役前)会在一天以内回复的!(你也能够去问更强的boshi和litble,别去问zyf由于他已经退役了。)
题单我就不给了,由于网上有不少不少。。。
固然,若是你立志要当大佬。。。那赶忙打开陈立杰的ppt吧=。=
感谢您的观看qwq!
原文出处:https://www.cnblogs.com/sclbgw7/p/10197629.html