To_Heart—题单——SAM

感觉SAM挺神仙的,所以准备列个题单。不定时更新?

感觉SAM最重要的是它的可以不重不漏遍历完所有子串的性质。很nb啊。但是但是要不咱们背板子吧!

感觉 cmd 大佬一句话非常正确

SAM模板的学习原则 : 理解性默写。

【模板】后缀自动机 (SAM)

link.

大概是明白了。一开始想得有点复杂甚至有点傻逼。

你会发现任意一个字串一定

[SDOI2016]生成魔咒

link.

你发现构建 SAM 是个在线的过程。所以直接在每次在线更新的时候更新答案就好了。

[TJOI2019]甲苯先生和大中锋的字符串

link.

先找到所有出现次数为 k 的节点。然后考虑每个节点覆盖的子串长度为 (t[t[i].link].len,t[i].len] ;然后差分加前缀和就好了。多组输入有点恶心。

CF802I

link.

你发现这个和板题其实没什么区别,只需要在树上dp的时候把ans更改的条件变一下就好了。

[TJOI2015]弦论

link.

考虑从根节点遍历整棵树,如果这个遍历到这个点时知道他从 a ~ z 的所有点分别可以出现再几个子串中,那么我们直接类似于 Trie01 串求异或最大值的方式找到第 k 大就好了。所以现在的问题是如何找到经过每个节点的字串个数。

首先考虑 t=0 的情况。t=0 时只有本质不同的子串才是不同的。然后考虑SAM遍历的话是可以不重不漏的遍历出每个子串的。那么经过某个点的子串个数就相当于这个点能到达的字串个数。

然后考虑 t=1 的情况。这时候位置不同就是不同的子串。考虑一个子串可以出现几次。发现一个子串最多出现次数就是 它对应的endpos集合的大小。所以把每个点的贡献从 1 改为 对应的endpos集合的大小 就好了。

最长公共子串

link.
应该是个大的板块?

大概就是对于第一个字符串,建出SAM,第二个字符串在上面跳

  1. 如果当前节点有关于 s i s_i si 的转移,那么直接往上面跳,当前匹配长度+1
  2. 如果没有,就在 Parent Tree 上往父亲跳,知道找到一个节点有关于 s i s_i si 的转移为止。如果找到了,那么当前匹配长度 = 跳到的节点的 len+1,再往 s i s_i si 的转移上跳;如果跳不到,就重置当前节点为0,当前匹配长度为 1 。

这么跳为什么是对的?因为考虑 Parent Tree 上的每父亲节点是儿子节点的后缀,而 SAM 转移方式中的当前节点一定是下一个节点的前缀。或许这才是SAM的最重要的性质?

[BJOI2020]封印

link.

应该是做的第一道不全是SAM的题?

发现在最长公共子串中我们的算法实际上是算对于匹配串的每一位,从它开始往前和模式串的最长公共子串长度,然后再对所有位置的最长长度取max。

那么我们定义 L e n i Len_i Leni 表示从 i i i 位置开始往前和模式串的最长公共子串长度,用类似于最长公共子串的算法,可以在 O ( n ) \mathrm{O(n)} O(n) 的时间复杂度以内预处理出来。

然后你会发现一个性质:设 l i = i − L e n i + 1 l_i= i-Len_i+1 li=iLeni+1 ,那么 l l l 数组是单增的

所以可以在询问的 l , r l,r l,r 之间二分 m i d mid mid,使得 m i d mid mid 是最小满足 l m i d ≥ l l_{mid} \ge l lmidl 的位置。

那么对于 mid 之前的点,它们对于答案的贡献小于等于 ( m i d − 1 ) − l + 1 (mid-1)-l+1 (mid1)l+1 对于 ( m i d , r ) (mid,r) (mid,r) 的点你随便写个数据结构取出来最大值和 m i d − l mid-l midl 比个大小就好了。


以下内容皆为口胡,有时间再写代码(

[]
posted @ 2023-03-23 22:01  To_Heart  阅读(1)  评论(0编辑  收藏  举报  来源