CF2196E:SA的应用
CF2196E:SA的应用

首先贪心的思路是显然的,因此我们相当于在t串中不断求最长可错配lcp
法1:根号分治
我们考虑以 分块
- Case1:对于 的情况。(即一次最多走 步)
我们可以先预处理 中所有小于等于 的子串,并枚举它的哪个位置是空的。接下来把这个串挖掉这个空直接丢入hash中(相当于我们枚举的这一位直接赋值为0),这样的复杂度是 的。
然后我们现在处理 串。我们可以枚举每一个被替换的位置 ,那么显然 只有 种可能。然后我们再去枚举 。
但枚举 的这个过程可以优化。假设对于一个 ,如果 可以,那么 一定可以。因此我们可以维护当前最长 的 ,在 不断右移的过程中,去试试 是否可行(因为更短的就没必要了)。在这种情况下, 和 构成了two-pointer,因此一次枚举的复杂度就是 的。因此这一部分的复杂度是 。
- Case2:对于 的情况
在这种情况下,我们可以直接枚举 串的开头 (显然要枚举 次)。然后对于每个 ,我们让其与 取一次LCP(这个可以SA+RMQ预处理),然后跳过LCP+1的位置,再取一次LCP,就可以求出它们两个的最长长度了。这样一次的复杂度是 的。
而我们只有在Case1中发现 至少可以取到 ,我们才会进入这一轮的判断。因此只要进入了这一轮的判断,至少会走 步。因此这部分的复杂度是 的。
因此这种做法的总复杂度是 ,可以取 ,可以通过easy version
法2:SA + 二分
首先我们通过预处理SA,可以对于当前的 ,在 的时间内找到与 所有后缀的LCP的最大值。
接下来有一个比较明显的性质:我们修改的这个位置,一定在小于等于这个最长LCP+1。
因为我们可以枚举这个修改的位置。枚举的总次数是 的,因为这一轮我们至少会走LCP步。
枚举完修改位置,我们也可以直接枚举成替换成哪个字母。不妨假设在 位置进行修改,修改完后待匹配的串是 。(注意这个 是从 位置开始的)
接下来我们想要寻找的是 和 所有后缀的最长LCP。
去找这个最长LCP,我们可以在 串自己的后缀数组中,找到满足 ,然后分别求 与 的LCP即可。
对于找 的过程,我们可以采用二分的办法。以找 为例,我们直接二分。然后求原串 和当前 的LCP,不妨设为 。
- 若 ,我们可以直接比较出 与 的大小关系(在 位比较一下即可)
- 若 ,我们也可以直接比较二者的大小关系( 和 在这种情况下等价)
- 若 ,我们可以再求一次 和 的LCP,这也是 的。
因此,我们可以在 的时间内求出 。同时,在上面的过程中,我们其实已经实现了求LCP的过程了。
总复杂度




