洛谷P5576 [CmdOI2019]口头禅(字符串)

洛谷P5576 [CmdOI2019]口头禅(字符串)

题目描述

蒟蒻出题人收集了某位大佬的\(n\)条语录,并按照时间排序,编号为\(1...n\)

他发现这位大佬的口头禅是随着时间而变化的,而且里面有些看不懂的内容。

在请教了群DS带师之后,他得到了某种hash方法,把这些语录都变成了01串,这样好懂一些。

为了研究水群的奥秘,他进行了多次询问:\([l,r]\)之间的所有语录,最长公共子串的长度是多少?

数据范围

subtask编号 n m 语录总长 分值
1 50 50 500 10
2 50 50 80000 15
3 2000 10000 160000 15
4 20000 \(10^5\) 400000 15
5 20000 \(10^5\) 400000 45

对于subtask4 : 语录生成后,之间的顺序经过随机打乱。

对于subtask5 : 空间限制为50Mb,其他的数据为500Mb

解题思路

后缀自动机

首先先膜出题人 @command_block

很神奇的思路

首先要会用后缀自动机来求多串的最长公共字串

两个做法,一个是对一个字符串建后缀自动机,然后将其他串在上面打标记,记录匹配到节点 x 的最长长度是多少,另一个方法是选一个字符串 S,剩下的分别建后缀自动机,然后这个字符串分别在其他串上跑,记录 \(f[x]\) 表示 S[1...x] 能够匹配的最长后缀是多少。本题两种方法均可。

那么神仙的分治就来了

倍增分治

对于一个区间,将长度小于等于 \(2^x\) 的串提出来进行分治,直到子区间没有这样的串为止,这时候我们 ++x 继续此过程

显然我们有一个 x 最大深度是 \(\log N\) 的,又一共有 \(\log N\) 个 x,总分治深度是 \(\log^2 N\),但实际上并跑不满

为什么要这么分治,显然我们使用后缀自动机时选出的字符串大小越小越好,这样我们才能方便的记录前缀信息和后缀信息,否则空间都开不下

询问答案

考虑跨过区间 mid 的答案,记录从 mid 开始的前缀 f 和后缀 f,查询时合并答案即可

时间复杂度证明

考虑区间字符串长度总和为 len,区间长度为 k,最小的串长度则不超过 \(\frac {len}k\)

扫一遍此区间的复杂度为 \(\Theta(k * \frac {len}k) = \Theta(len)\),所以分治扫所有的区间记录前缀后缀时间复杂度是 \(\Theta(len\log^2N)\)

如果串长小于 \(\frac {len}{\sqrt m}\),有复杂度 \(\Theta(len\sqrt m)\)

如果串长大于 \(\frac {len}{\sqrt m}\) 则无法保证复杂度,但很好的发现 k 小于 \(\sqrt m\),想一想,为什么,也就是本质不同区间有 \(k^2\) 个,记忆化一下可以得到 \(\Theta(m\log m+k^2*\frac {len}{k}) = \Theta(m\log m+len*k)\)

代码就先不放了

另一种思路-SA做法

这题 SA 被卡空间了,所以我懒了还没有写,但不妨作为一种启发,如果有 dalao 发现了什么问题欢迎来踩和讨论,毕竟我只是口胡

感谢 @z7z_eta 的帮助

SA求多串最长公共子串

看到这题你应该有些基础,我就稍微简略些吧

将所有串首尾相连拼起来求 SA 数组,将 height 数组看成隔板,从大到小拆,直到所有串的后缀均在某一个区间内出现,这个 height 就是答案

如何看所有串均出现在某一个区间呢

set 启发式合并

每个位置开个 set,维护当前联通块的 “颜色” (每个原串是一种颜色)集合,当然我们维护区间会好一些,比如 2,3,4,5 颜色可以直接压成 [2,5],在用上启发式合并就可以 \(\Theta(N\log^2N)\) 的维护了,注意启发式合并时不要看 set 的 size 而是颜色的个数

在本题中可以发现如果颜色区间 [l, r] 被某一次包含就是答案了,问题又来了,如何看 [l, r] 最早什么时候被包含呢

扫描线

不妨将区间看成二维平面上的点 (L, R),那么在 set 中合并时比如有 \([1, 3], [5,6]\),这时新插入一个区间 \([4,4]\),会使 set 中的区间融合 \([1,6]\),那么新的大区间所包含的所有小区间答案和当前的答案取 max 就行了,那么 \([1, 6]\) 可以看成二维平面上的一个矩形 \([1,6],[1,6]\),对里面的点取 max 即可,发现所有的点都在 \(y = x\) 的上方,矩形可以换成 \([1,+\infty],[-\infty,6]\),这样扫描线加树状数组即可维护,单独是 \(\Theta(n\log n)\) 的,但事实上启发式合并会产生 \(\Theta(n\log n)\) 个矩形,所以总复杂度 \(\Theta(n \log^2n)\)

时间复杂度 \(\Theta(len\log len+len\log^2 n+n \log^2 n)\),空间复杂度 \(\Theta(n \log n)\)

这个做法比较好想,但应该没有 sam 快,但那个太难想了啊

posted @ 2020-06-13 22:09  Hs-black  阅读(265)  评论(0编辑  收藏  举报