Luogu5576 [CmdOI2019] 口头禅
题意:给出
共有
本题的 SAM 上两个自然根号做法,以及一个 polylog 做法。
算法一:分治
前置知识:用 SAM 求
个字符串的 LCS。 任取一个字符串
作为基准匹配串,剩下的是文本串。 对于其中一个文本串
,令 表示 中 在 中出现过,即以 结尾的位置的最长匹配长度。 这是经典问题,用
在 的 SAM 中匹配一次即可求得,故不赘述。 最后将所有匹配串所得到的
取 ,最后把整个数组取 即可。 记串长总和为
,建 SAM 的复杂度为 , 要在每个 SAM 上跑匹配,复杂度 。总复杂度为 很明显,选择这些字符串中长度最短的串做基准串,跑的最快。
没有强制在线,考虑猫树分治。
注:把分治树存下来,类似于猫树就可以做到强制在线。但所需空间较大,并不实用。
分治到某个区间
以
若分治时选择的
对于阈值
,称长度小于等于 的为短串,长度大于 的为长串。记总串长为 ,显然长串的个数为 。 分治到某区间之后,找出所有短串,取其中间位置做基准串。这样分治直到区间里都是长串,将阈值
增加 。
接下来计算该算法的复杂度。
求
观察分治树,对于一个
,对应的短串在 层分治后就被耗尽。 阈值为
时,分治区间的总大小是 的,基准匹配串的长度为 ,故花费的时间为 。 阈值
共有 个,故总复杂度为 。 处理询问
分治中,合并答案的复杂度为
。 观察上面的分治,不难发现,一个询问区间所对应的基准串,不会超过区间中最短串的
倍。 这里会产生一个自然根号。对询问记忆化后,复杂度为
。证明如下: - 结论:数列
和为 ,询问 的代价是 。记忆化后, 次询问的代价不超过 。
该结论可以加强为
结论:数列
和为 ,询问 的代价是 。记忆化后, 次询问的代价不超过 。 证明:设定阈值
,称大于 的元素为大元素,否则为小元素。大元素不超过 个。 若
之一是小元素,询问代价不超过 。 若代价大于
, 必须都是大元素,这些询问的代价总和是
总复杂度
,令 得最优复杂度 。
- 结论:数列
综上所述,时间复杂度为
所有操作均为暴力 for
和取
利用
数组也可求出区间本质不同公共子串数目。留做习题。
算法二:广义 SAM + 扫描线
对于广义 SAM 上的节点
当询问区间
序列扫描线。考虑逐步增大
对于 SAM 上的点
在询问
这里又有一个自然根号:广义 SAM 上
于是,在
有
卡常后可以通过本题。
算法三:广义 SAM + 线段树
可见 (Mina)【题解】[CmdOI2019] 口头禅 广义 SAM -永无岛
这里也简要地介绍一下该做法。
用 std::set
维护
按照节点的
每次合并后,若产生新的连续段(该事件最多会发生
使用线段树维护询问,将询问
需要寻找
复杂度为