题意:给你n个串,求它们的LCS。
后缀系列的经典问题。
这道题用后缀数组+二分当然可以做。将n个串连接成一个长串,只需要每次二分LCP,看是否有n个后缀LCP大于该值,又正好分属n个串就好。
但这个复杂度显然是$O(n \log n)$的,在这里过不了。我们考虑用SAM解决这个问题。
首先我们从两个串的情况入手: (SPOJ LCS正是两串LCS的题目)
将第一个串$s_1$的SAM构建出来,然后在上面运行第二个串$s_2$。记录一个到当前位的LCS长度,记作len。如果当前到了第i位,节点u有$s_2[i]$的转移边,则走转移边,len长度+1。如果没有该转移边,由于next边上节点所包含的串均为当前点u包含串的后缀,所以沿着next边向前走,走到第一个有该转移边的节点v。根据节点的定义,一个节点有转移边,则其中所有串均可以通过这条转移边到另一个合法子串。于是我们只需要取最长的一个。即走过v的转移边,将len重新赋值为$Max[v] + 1$。如果走到根都没有转移边的话,就从根重新开始,并将len赋值为0。每走一步更新答案即可。
如何处理多个串的情况?我们对于每个节点多记录两个值,一个代表所有串运行到当前点时的LCS值,一个代表当前串运行到当前点时的LCS,即上面的len。注:一个串可能被运行到一个点多次,记录最大值即可。要注意的是,在之前的双串LCS中,我们忽略了一个东西:当我们运行到一个点,即代表这个点包含的串在原串出现时,该节点通过next边连接的所有点,即该串的所有后缀也都在原串出现了,所以之前的点的len值也需要更新。在两个串的情况里,运行到一个点时,其代表串的后缀显然不会更优,并不用在意这一点。但是在多串中,我们需要考虑其他串的影响,就要基数排序排出节点间的拓扑序,按照其逆序更新所有节点的真正len值。运行完一个串后,将每个节点的所有串运行到当前点的LCS值对当前len取min即可。(如果没运行到则len=0,说明该节点代表的串不会参与答案更新)
最后运行完所有串后,所有节点LCS的max值就是答案了。
这道题卡时间,多清空几个数组都会T。不过错误算法(不基数排序)可以水过去。。。