[CmdOI2019]口头禅 广义SAM

前言

这个idea不是我的,是之前在UOJ群里面有群友提出的一个做法,我这里只是根据当时提到的大致想法,结合自己的理解整合成了一个我认为较详细的可行方法。
(不好意思,我是真不知道那位群友是谁,如果看到了的话麻烦告知一声,会补上id的)

正文

1.建广义SAM

我们要解决的是多串公共子串问题,这个有很经典的数据结构来应对-广义SAM。

2.转化题意

现在,广义SAM已经建出来了。
那么,我们发现,对于一个节点,它所代表的串是一个串的子串当且仅当它的子树中的叶子节点有这个串的一个前缀。
换句话说,如果我们在插入的时候给每个节点染上颜色(每个串染不同的颜色,而且一个节点可能有多种颜色),然后令每个节点的权值为\(maxlen\),那么问题就转化为,在所有满足子树中包含\([l,r]\)中的所有颜色的节点中,权值最大的节点的权值。

3.解决转化后的问题

子树数颜色,而且还要查询是否有连续一段的颜色,这让我们想到对每个节点维护子树中出现的颜色的连续段。
维护一个子树中出现的颜色的连续段,就是把子树的所有的信息合并,再插入这个节点上的颜色。
合并颜色连续段,可以用启发式(直接按照子树大小启发),因为 颜色段数 \(\leq\) 颜色数 \(\leq\) 子树内节点个数,所以完全可以当做启发式合并的复杂度分析,这里维护连续段需要一个数据结构,支持\(lowerbound\),任意位置插入,目前没有想到比 \(set\) 在复杂度不更劣的情况下码量更少的写法。
然后考虑回答询问,因为每个点的权值是 \(maxlen\) ,我们可以从大到小枚举 \(maxlen\) ,当我们枚举到一个节点时,由于它的子树内的所有点的 \(maxlen\) 必然大于它自己,所以它的儿子一定已经被合并好了,直接合并即可。注意,我们这里回答询问的方式是,每当我们合并出一个新的颜色连续段,我们就去一颗二维线段树上查是否有新的可回答询问,如果有就回答,然后将这个询问删除。

4.复杂度分析

我们的算法的复杂度主要在以下几步:
1.广义SAM
2.启发式合并
3.查询是否有新的询问可以回答
4.插入新的颜色
第一块显然不是瓶颈,第二块的复杂度是\(O(lenlog^2)\),第三块,由于每个询问只在二维线段树上贡献\(O(log)\)的复杂度,所以这一块的复杂度是\(mlog\)的。第四块,因为总插入次数不超过总串长,所以总复杂度也是\(O(lenlog)\)
所以总复杂度是\(O(lenlog^2+mlog)\)的。

posted @ 2020-06-18 22:03  永无岛  阅读(204)  评论(0编辑  收藏  举报