这是第不知道多少次学SAM…感觉以后还要忘赶紧留个笔记..
欢迎指出错误
SAM有两个东西,一个是trans,一个是parent树
第一个东西的作用,就是如果你从根节点开始,走一个串,如果这个串是原串的子串,就一定能够走到这么一个点,他的parent树里的子树里的right的并集就是这个串在原串出现的所有位置,如果这个串不是子串,就不能够走到一个点
parent树,可以把它理解成反串的后缀树,
因为它从根走到底,就是在原串的所有right位置,向他前面添加原串里在他前面的那些个字符,添加了若干个后他们不同了,那么他们在parent树上就分到了某个节点的不同的子树,注意是若干个,因为完全可能在这个节点的right集合里,给他们前面添加了许多字符他们仍然一样,那么这些添加了不同字符的串被缩成了一个点,因为虽然这些串不同,但他们在原串里出现的所有位置的右端点是一样的,完全可以把他们放一起处理,
然后从底走到根考虑它,实际就是许多前缀,他们去掉他们的一部分前缀(这一部分要最短)后相同了,这些点就跳到了一个共同的父亲,于是这个父亲可以代表他们的公共最长后缀,然后以这个点代表这些串剩下部分,继续去掉前缀和其他点匹配。
那么反着理解,parent树从根至底就变成了不断延长后缀,走向不同的分支,从底至根,就是若干个后缀匹配最长公共前缀
于是很多后缀相关的题目,除了用SA外,还可以建出反串的SAM,就得到了后缀树,后缀间的最长公公前缀,就是他们在后缀树上的LCA的长度
SAM一般的题目,字符集都是所有小写字母,但也有些题会是数字,于是就会有一个不小的范围,不可能给每个点都开这么大的son数组,这时需要给每个点的son开成map
广义SAM:
实际上就是在一个trie上建SAM,每个串插入时last重新指向root,这时建法与普通的SAM唯一的不同点,就是last可能已经含有son[w]这个孩子,这时仍然像正常建的时候那样,看这个孩子q的dist是否=last.dist+1,如果是,last就可以指向这个q,否则因为长度不同,仍然需要新建一个np代表当前前缀且需要分裂这个q,last指向np
广义SAM的题挺多是问如一个串被多少个串包含的,对SAM的每个点都开一个vector,对每个串建出的所有前缀,链表里记录下这个串,这时我们建出parent树,走到可以代表询问串的节点,那么问题就转化成了询问这个点的子树里有多少种颜色,弄出dfs序,就变成了询问一个区间有多少种颜色,离线后差分一下就可以用树状数组做了
确实字符串的题都蛮套路的