后缀自动机总结

学了一发后缀自动机……然而特么做题全是看的题解(毕竟pb太弱)……
这里就不写后缀自动机的构造和原理了吧……网上多的是……(事实上是pb太弱不懂……)
1.codevs 3160最长公共子串
题目都一句话了……我就懒得说了……
这是后缀自动机必做的裸题吧……
先把第一个字符串建一个后缀自动机,然后拿第二个串在后缀自动机上跑,优先跑能走到的字符,如果走不到就走pre节点,再看能不能走这个字符,如果pre节点跑完了都没法走的话,就把现在长度赋为0继续跑下一个字符,每次现在长度变大时都更新一下答案。
2.codevs 1372DNA
cv上这题的数据很水……乱搞都能A,但如果想弄懂SAM的话,请认真对待每一题。
这题实际上也就是一个求最长公共子串……把给你的那个字符串取反后倒过来再跑原串建出来的后缀自动机就好了。
3.codevs 1500后缀排序
题意:给你一个串,然后对它的所有后缀从小到大排序,按排序后的顺序输出每个后缀第一个字母在原串的位置。
我们可以先建出后缀树,也就是SAM的pre树,然后我们标记一下每个节点是否是原串上的点(换句话 说,标记这个点是否是个复制后的点),然后直接按字典序跑一遍就好了。
4.UOJ#35后缀排序
这题比上一题多了一个要求排序后任意两相邻的后缀的LCP,那么这个实际上就是SA的height数组,所以更像是SA的模板题,但是SAM也是可以做的,我们在后缀树上跑的时候,记录一下LCA的所表示的节点的step,由于后缀树上的叶子节点必定是一个后缀,所以我们在dfs到某个点的时候,如果它是一个原串上的点,我们直接更新step的值,之后先dfs,再把step改为当前节点的step。
5.BZOJ 3238差异
这题跟上题也有点像……只不过我们需要记录一个子树的size……以及子树中每个后缀的长度和,然后dfs到x这个点的时候,对于每个son,就用乘法原理就好了。
6.BZOJ 4199品酒大会
有了上一题的基础那么这题也比较容易了,上题是记录子树和,这题记录子树最大值就好了,由于有负数,所以还要记录一个子树最小值。为什么不要记录次大和次小?因为你在每个节点都更新一遍答案的话,所有情况都可以只用当前子树的最大值和最小表示出来,至于为什么可以考虑一下后缀树的结构。
7.SPOJ LCS2
题目大意是说有n个由小写字母组成的字符串,求他们的最长公共子串。
我们考虑做只有两个字符串的LCS时候的做法,那么n个字符串的话,我们只要对于SAM上的每个状态都记录一下第i个字符串到当前状态的最长公共子串的长度是多长即可,然后对于每个状态上的n个值取min,最后再对每个状态取max就是答案,注意由于当前节点能接受的后缀pre必定能接受,所以我们需要在pre树上求子树最大值,这才是第i个串在当前状态的答案,注意细节。
8.BZOJ 4566找相同字符
这题既可以用单串SAM也可以用多串SAM,先说多串SAM的做法。
不难发现对于SAM上的一个状态,如果两个串都能匹配到当前节点,那么这两个串在这个节点就是相同的,于是我们对于两个串分开记录他们的right集合大小,right相乘再乘上当前节点代表的字符串个数之和就是答案了,当前节点代表的字符串个数之和是当前节点的step-它的pre的step(原因在丽洁姐姐的PPT上已经讲的很详细了)。
接下来讲单串SAM的做法。
我们先对第一个串建SAM,考虑第二个串怎么办。首先,我们可以直接把第二个串放在后缀自动机上运行,类似于求LCS那样,这样的话,我们对于当前状态,如果它包含了整个节点(也就是匹配长度=当前节点的len),我们称该节点为完整节点,否则为不完整节点。那么对于不完整节点的状态,我们可以直接计算它的字符串个数,而对于完整节点的状态,我们需要先打上标记,表示这个地方被完美匹配了一次,最后在pre树上dfs,对于一个节点,它的贡献是子树中的标记数量它的right集合大小它所代表的不同字符串个数。这个是为什么呢?pre还有一个性质,那就是pre代表的字符串必定是当前节点的后缀,因此当前节点的出现次数pre显然也是出现了这么多次的。
9.ICPC camp2017某题
题目大意:给定一个由小写字符组成的字符串,求它有多少个子串满足循环左移一位后还是原字符串上的子串,n<=3e5。
这题出的非常妙啊……考察了很多对SAM的理解……
显然我们需要枚举一个东西,在不TLE的情况下,那自然枚举后缀自动机的节点是坠吼的。
我们来考虑SAM的性质,首先,如果当前节点的next数组有某个字符,那么说明这个字符接在当前节点组成的字符串的后面也是一个原串的子串。
其次,对于一个节点,它所能代表的不同的字符串个数,假如不是最长的它所能代表的字符串,那么它的前一个字符是固定的,这为我们循环左移提供了方便,对于这个固定的字符,我们可以直接看它的next数组中是否有这个字符。
这样,我们得到了一个初步的算法,枚举SAM的节点,设当前枚举的节点代表的字符串为s,对于每个不是当前节点表示的最长串的字符串,直接判断SAM的next数组中是否有这个字符,但是我们发现一个问题,那就是直接枚举节点所能代表的字符串复杂度是n^2的,那不妨换一种角度,我们枚举当前节点的后一位,那么当前节点所代表的字符串实际上是一个区间,那么用前缀和可以O(1)求出每个区间的某个字符的出现次数,这样,我们相当于是枚举sc,(c为一个字符),求是否有cs在原串中出现过,注意求区间的时候判断一下pos

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值