KMP模板推导

两串匹配问题

假设我们有两个长度分别为 \(n,m\) 的串 \(s,t\)。我们现在要找出 \(t\)\(s\) 中所有出现位置的左端点下标。

枚举左端点暴力匹配的复杂度最坏是 \(O(nm)\),用单一字母可以构造出数据。

但是我们发现,对于前面的匹配区间,后面的匹配区间和它可能有两种关系:

  1. 无交
    两者求解独立,同时不影响复杂度。
  2. 有交
    相交部分对于前面的区间是个后缀,对于后面的区间是个前缀。
    但是这前、后两个区间都是能和 \(t\) 串匹配的。
    所以相交部分就是 \(t\) 的一个 \(\text{border}\)

所以我们现在就要知道一些 \(\text{border}\) 的性质,来方便维护这个东西。

关于 \(\text{border}\) 的性质以及具体匹配方法

关于 \(\text{border}\) 有个显然的性质

对于一个串,它 \(\text{border}\)\(\text{border}\) 也是它的 \(\text{border}\)

假设我们在 \(s[l,r]\) 的位置和 \(t\) 产生了一次成功的匹配。

由于后面的匹配区间和 \(s[l,r]\) 的交必定是 \(s[l,r]\) 的一个 \(\text{border}\)
但是这个 \(\text{border}\) 不一定是最大的那个。
为了方便处理,我们考虑先将当前变成最大的那个 \(\text{border}\),然后逐渐变小来判断合法性。

现在问题是怎么维护。考虑维护一个最大 \(j\),使得 \(s[r-j+1,r]=t[1,j]\)。每次将 \(r\) 这个右端点往右移动,
相对应的为了满足条件,我们会尝试不断地让 \(t[1,j]\) 变成其最大 \(\text{border}\),使得其下一个字符能匹配上,

\(t\) 整个串再一次都匹配上时,就重复这个过程。

现在问题就只在于,怎么对每个 \(t\) 的前缀,预处理出其最长 \(\text{border}\)

\(t\) 每个前缀的最长 \(\text{border}\)

我们记要求解的东西——所有 \(t[1,i]\) 的最长 \(\text{border}\) 长度为 \(nxt_i\)

一个对 \(nxt\) 数组的理解:\(nxt_i\) 表示的不只是 \(t[1,i]\) 的最长 \(\text{border}\),也是在当前无法匹配第 \(i+1\) 个位置时的失配指针。

我们考虑增量法求解。每次利用前面的信息处理 \(nxt_{i+1}\)
具体的,在尾部加入一个字符后,可能会有一些原本是 \(\text{border}\) 的对应串现在仍是 \(t[1,j]\) 往后加一个字符后的 \(\text{border}\),也有一些不再是当前位置往后的 \(\text{border}\)

故而我们利用已有的所有 \(nxt\) 信息,从大到小访问所有 \(\text{border}\),并且找到最大的合法 \(\text{border}\) 并记录。

复杂度

空间复杂度 \(O(n+m)\)

时间复杂度,考虑每次右端点往右移动 \(1\),维护的 \(j\) 最多加 \(1\)
而每次跳 \(\text{border}\) 的时候 \(j\) 至少减 \(1\)
故而均摊时间复杂度为 \(O(n+m)\)

posted @ 2025-02-24 20:15  徐子洋  阅读(13)  评论(0)    收藏  举报