看了几天的后缀自动机,感受这玩意儿确实比较神奇。可是感受本身确定讲不明白,就简单的来写写心得和应用吧html
一、每一个状态$s$表明的长度区间为$(len[fa[s]],len[s])$算法
也就是说$min(s) = max(s) + 1$post
二、每一个状态$s$表明的全部串在原串中的出现次数及出现位置右端点相同。url
这也是后缀自动机可以压缩状态的缘由,就是把不少相同的串压缩到一个节点中spa
三、在parent树中,对于状态$s$,$fa[s]$所代表的状态是$s$所表明状态的后缀htm
四、在parent树中,每一个状态的$right$集合是它父节点$right$集合的子集blog
由性质$3$很容易获得$fa[s]$所表明的状态是$s$所表明状态的后缀,那么$fa[x]$所表明的子串的出现位置必定比$s$表明子串的出现位置多排序
五、对转移边造成的DAG拓扑排序后,每一个节点对应的大小为:以该节点为起点的子串的数量(本质相同的子串算一个)字符串
六、对$fa$边造成的树拓扑排序后,每一个节点对应的大小为该节点对应$right$集合的大小get
$fa[s]$表示的是$s$的前缀,那么$s$出现的地方$fa[s]$也必定出现
首先把第一个串的SAM建出来,
枚举第二个串,同时沿着转移边进行匹配,若匹配失败,那么就沿着$fa$边向上走,
匹配的同时记录一下$max$
网上的作法基本都是对第一个串建SAM,而后枚举其余的串,在这个串上匹配。
可是貌似要考虑不少东西??
这里我将一个比较naive可是不会TLE的作法。
最终的答案必定出如今第一个串中,因此能够把第$2-N$个串的SAM建出来
而后枚举第一个串的每一位,在其他的SAM中匹配。
虽然听着吓人,可是代码十分好写
咱们能够经过对转移边$dfs$而求出以该节点为起点的子串的大小
开始时从$root$开始走,每次优先选择字典序小的转移边,
若该出边对应的大小$<k$,说明答案不在该出边所对应的字符串中,令$k$减去该节点的大小,继续匹配
若该出边对于的大小$>=k$,说明答案在该出边中,那么沿着该出边继续走
注意在求第$k$小子串的时候要考虑本质相同的子串是否重复统计的问题
若是要重复统计,则加上$right$集合的大小就能够了
最小表示法的定义:
字符串$S$的最小表示法为,对于任意的$i \in [1, |S|]$,把$[1,i]$对应的字符串剪切到$S$尾所造成的字符串中,字典序最小的一个
字符串的最小表示有它本身的算法,能够参考这里
固然后缀自动机也是能够搞的,咱们首先把字符串复制一遍,扔到SAM里,
而后从根节点出发贪心的走较小的出边,同时输出每一次通过的字符,当达到$N$次时中止。
可是我仍是建议你们学一下最小表示法的标准算法, 由于后缀自动机须要$4*|S|*siz$的空间($siz$表示字符集),很容易被卡掉
考虑到每一个状态表示的子串是两两不一样的,
根据性质1每一个状态$s$表明的长度区间为$(len[fa[s]],len[s]]$
而后对全部节点求个和就好,答案为$\sum_{t} len[t] - len[fa[t]]$