给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。当这两个子串中只要有一个取得位置不同时,两个方案不同。
链接
题解
解法一:$O(n^4)$
暴力枚举两个起始位置,然后枚举每个起始长度, $O(n)$ 的判断子串是否相同,这个算法是 $O(n^4)$ 的。
解法二:$O(n^3)$
暴力枚举两个起始位置,然后从 $1$ 到 $n$ ,每次判断新增的一个字符是否相同,从而判断子串是否相同。这样对于每一个起始位置的判断就是 $O(n)$ ,最后的复杂度就是 $O(n^3)$ 。
解法三:$O(n^2)$
我们学会了后缀数组,我们知道了我们事实上可以在 $O(n\log n)$ 预处理的情况下 $O(1)$ 的得到解法二的 $O(n)$ 的过程,也就是求一下 $LCP$ 。这样的话,复杂度是 $O(n^2)$ 。
解法四:$O(n \log n)$
我们先转化一下问题。这道题要求的是两个串的每一个位置两两之间的 $LCP$ 的和。但是如果我们枚举的话,时间复杂度至少是 $O(n^2)$ 。那么我们肯定要用一些数据结构之类来批量求和,最后才能够降低复杂度。
其次,我们发现这个问题可以拆解。我们只需要找出一个解法,解得在一个字符串里面任取两个位置不同的子串,取得子串相同的方案数。
令 s_3 = s_1 + "?" + s_2
,那么答案就是 cal(s3)-cal(s1)-cal(s2)
,其中 ?
是一个没有在字符串里面出现的字符。
那么我们发现,对于每一个位置来说,我们可以将其视作以这个位置开始的后缀,那么其顺序对于每一个位置两两之间 LCP 的和是无关紧要的。
所以我们按 $sa[i]$ ,也就是后缀字典序的顺序来遍历。每次我们都要求这个位置和前面所有位置的 LCP 的和。那么这个时候,我们就可以把前面的所有后缀到按字典序前一个后缀的LCP长度扔到一个 Splay 或者什么权值线段树里面去。
然后这个时候我们新加入了一个后缀,需要更新这个数据结构。我们需要把这个数据结构里面所有的大于 $ht[i]$ 的数都拎出来,改成 $ht[i]$ ,然后再塞回去就可以了。然后每次给 $ans$ 加上这个数据结构里面所有数的总和就可以了。
这个算法的时间复杂度应当是 $O(n \log n)$ 。
解法五:$O(n)$
什么???这种东西还能 $O(n)$ ???
反正我很震惊。
于是我就在合格考的考场上苦思冥想,最后自己脑补出了一个数据结构。用摊还证了下复杂度,竟然发现是 $O(n)$ 的…仔细一想,这个东西叫单调栈2333……
其他都同上,我们现在解决的是这里:
我们需要把这个数据结构里面所有的大于 $ht[i]$ 的数都拎出来,改成 $ht[i]$ ,然后再塞回去就可以了。然后每次给 $ans$ 加上这个数据结构里面所有数的总和就可以了。
怎么办呢?我们想能不能暴力解决这个问题。注意到我们每次用 $ht[i]$ 更新之后,所有的这些数我们都可以只用一个数(数对)来表示,也就是 $(ht[i],cnt)$ 。我们维护一个有序表。然后每次从大端把所有大于等于 $ht[i]$ 的数给拿出来,更新 $cnt$ ,最后在插回去一个新的节点。
然后数据结构里面的数的和的更新就比较简单了…记一下出来的数的和,再记一下进去的数的和,然后加一下减一下即可。
可以用摊还证明,这个东西是 $O(n)$ 的。
我用的后缀数组是 $SA-IS$ 算法,也是 $O(n)$ 的。
语言很混乱,哪看不懂可以问我23333
代码
|
|