学会了构建SAM之后,我们要开始学如何使用SAM来处理各种问题了.
我们先来整体看一下SAM的性质(引自2015国家集训队论文集张天扬《后缀自动机及其应用》):
1.每个状态s代表的串的长度是区间 (lenfas,lens] .
2.对于每个状态s,它代表的所有串在原串中出现次数和每次出现的右端点相同.
3.在后缀自动机的Parent树中,每个状态的right集合都是其父状态right集合的子集.
4.后缀自动机的Parent树是原串的反向前缀树 .
5.两个串的最长公共后缀,位于这两个串对应状态在Parent树上的最近公共祖先状态.
———————————————–线 割 分 是 我 >w<—————————————-
我们都说SAM能够完全代替SA.
因此我们先来看一下SA的应用.SA主要有以下几方面的功能(对每个功能我只侧重讲一下SAM的做法):
显然作为一个自动机,字符串的匹配功能是与生俱来的…因此SAM能够做字符串的匹配在显然不过…
SA在处理这个问题时候往往是建立一个height数组,其实也算是比较好实现了,但是比起SAM来略有不足.
首先我们将两个串反过来,那么求最长公共前缀就变成了求最长公共后缀.
在观察SAM的性质5,显然这个问题变成裸题了…
可能有人会疑问该性质的正确性,那么我们就来做一个简要的分析:
首先我们知道,对一个状态s,其父状态对应的串必定是s对应的串的后缀.这个根据SAM的构建方式想一想是很显然的.
所以找到两个串在SAM上的对应状态,再做LCA就能得到那个最长公共后缀也就是原问题的最长公共前缀了.
我们对原串构建SAM,求出每个点right集合里的最大值rmax.
对原串取反然后代入后缀自动机进行匹配,假设当前匹配串对应原串的子串是[l,r],那么如果这个区间覆盖到了当前匹配到状态rmax,则[l,rmax]是个回文串.(这东西看起来挺对的但是我不太会证,您自己画画图看一看也会发现这东西确实挺对的…)
同样的这种方法我们可以求出所有的回文子串来应对不同的问题>_<
对某一个串建立SAM,把其他串放到SAM上运行,时刻记录一下子串的长度,最后每个状态都把其他所有匹配串在此处的匹配长度取min,然后对所有状态的min取max即可.
—————————————————————–线 割 分 是 我 >w<—————————————————
这些都是比较常见的问题了.可能还有一些不常见的问题,然后我们又不太会用SAM处理,这种时候怎么办?
其实SAM可以线性构造后缀树和后缀数组.
我们知道SA的线性构造(DC3算法)不但常数大而且不好理解不好写,但是SAM线性构造SA其实是很简单的,而且常数小.
我们知道跟后缀数组对应的还有后缀树,在后缀树上做一遍DFS即可得到后缀数组.
那么如何利用SAM线性构造后缀树呢?
看性质四.
先看看什么是反向前缀树吧:
把每一个前缀的反串插入一个trie中,并把没有分支的链合并.
好吧其实这货可以看成原串的反串的后缀树..至于为什么大家可以自己看看论文或者脑补一下,我不多说了= =
所以我们对原串的反串建立后缀自动机,其parent树就是原串的后缀树了.
然后用前面说到的方法对后缀树DFS一下,得到的就是SA了.
这样看常数真的很小啊…基本上就是一个构建SAM,扫一下parent树,然后一个DFS的问题了…
并不会比DC3慢吧.
终于得到了我们满意的结果.
其实SAM的功能还有很多,大家都说他可以完全代替SA,而且好像功能更加丰富,但是由于我刚学会SAM没多久,对他还没有太深的理解,现在差不多只会这些了QAQ请大家见谅QAQ
更多的应用和上面那些应用的具体实现我以后会在题解里通过代码体现的.