[NOI2018]你的名字
题目描述
小 \(A\) 被选为了 \(ION2018\) 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了。
由于 \(ION\) 已经举办了很多届,所以在题目命名上也是有规定的,\(ION\) 命题手册规定:每年由命题委员会规定一个小写字母字符串,我们称之为那一年的命名串,要求每道
题的名字必须是那一年的命名串的一个非空连续子串,且不能和前一年的任何一道题目的名字相同。由于一些特殊的原因,小 \(A\) 不知道 \(ION2017\) 每道题的名字,但是他通过一些特殊手段得到了 \(ION2017\) 的命名串,现在小 \(A\) 有
\(Q\) 次询问:每次给定 \(ION2017\) 的命名串和 \(ION2018\) 的命名串,求有几种题目的命名,使得这个名字一定满足命题委员会的规定,即是 \(ION2018\) 的命名串的一个非
空连续子串且一定不会和 \(ION2017\) 的任何一道题目的名字相同。由于一些特殊原因,所有询问给出的 \(ION2017\) 的命名串都是某个串的连续子串,详细可见输入格式。
先考虑 \(l=1,r=|S|\) 的情况 \((68pts)\)
我们可以对 \(S\) 建出 \(SAM\),把 \(T\) 扔到上面跑匹配得到数组 \(L\)
如果不考虑本质不同这一限制,那么答案就是 \(\sum\limits_{i=1}^{|T|}i-L[i]\)
然后考虑怎么去重,我们同时对 \(T\) 建出 \(SAM\),同时对每个点处理出 \(pos[i]\),即该状态第一次出现位置
那么每个点对答案的贡献就是 \(\sum\limits_{i=1}^{tot}len[i]-max(len[f[i]],L[pos[i]])\)
然后考虑任意区间的情况 \((100pts)\)
发现我们需要快速得到一个区间的 \(SAM\),然而并没有什么好的方法
再仔细想想,我们真的一定要得到这段区间的 \(SAM\) 吗?
其实我们只需要借助 \(SAM\) 求出 \(L\) 数组
而观察 \(l=1,r=|S|\) 的情况,对于 \([l,r]\) 我们需要以下几种操作
-
1.查询点 \(x\) 在 [l,r] 内是否有 \(c\) 这个转移
-
2.跳 \(parent\) 树
-
3.得到一个节点的 \(len\)
发现其实在 \([1,|S|]\) 的 \(SAM\) 上也可以实现这些操作,主要问题在于怎么查询点 \(x\) 在 [l,r] 内是否有 \(c\) 这个转移
发现这本质上是求点 \(ch[x][c]\) 的 \(endpos\) 集合中是否有 \([l+length,r]\) 之间的点
处理一下 \(endpos\) 集合就可以查询了
还有就是我们跳 \(parent\) 前应该先减小 \(length\) 查询是否匹配
总复杂度 \(O(|S|log|S|+\sum\limits_{i=1}^Q |T_i|log|T_i|)\)