CF1202E
文章目录
由于直接计算每一对$s_i+s_j$的出现次数并不好做,考虑枚举它们分割的位置($s_i$结束$s_j$开始处)来计算总数。
因此任何在分割处结束的$s_i$都可以与从这里开始的$s_j$配对。
所以说我们只需要对每一个后缀计算从这里开始的字符串$s_i$的数量,以及对每一个前缀计算从这里结束的字符串$s_i$的数量。计算前缀可以通过反转$t$和所有的$s_i$转化为计算后缀。
所以我们需要对于每个位置$pos$计算从$pos$开始或结束的字符串个数。个人的做法是SAM,但官方题解有一种有趣的根号分治做法。
先确定一个阙值$MAG$。然后将字符串分为长或短两类:若$|s_i|\leq MAG$就当做短的,否则当做长的。显然不会有超过$\frac{\sum|s_i|}{MAG}$个长字符串。对于这些长字符串可以用Z函数或者前缀函数找到它的所有出现。这部分的时间复杂度为$O(\frac{\sum |s_i|}{MAG}+\sum |s_i|)$。
然后对于短的字符串考虑直接建一棵Trie。这棵Trie将会有$O\left(\sum |s_i| \right)$个点,但是最大深度为$MAG$。所以可以对于每个$pos$向下移动遍历Trie,同时计算出现次数,这只使用了$s[pos\ldots (pos+MAG)]$这部分子串。所以这部分的时间复杂度为$O(|t|\cdot MAG)$。
所以当$MAG=\sqrt{\sum |s_i|}$时,总时间复杂度为$O\left(\sum |s_i|+|t|\sqrt{\sum |s_i|} \right)$。这没有使用任何字符串数据结构。
然后讲一下线性时间复杂度的SAM做法。
对于$t$和$\operatorname{reverse}(t)$建出SAM,然后对于每个$s_i$和$\operatorname{reverse}(s_i)$在SAM上跑一遍,并对于它的结束位置的整个子树打个标记。最后直接计算每个位置存在的字符串数量就可以得到答案。
|
|