字符串合集I
1. 前缀函数与 KMP 算法
1.1 前缀函数
1.1.1 定义
给定一个长度为 \(n\) 的字符串 \(s\),其 前缀函数 被定义为一个长度为 \(n\) 的数组 \(\pi\)。
\(\pi_i\) 是,子串 \(s[0 \dots i]\) 最长的相等的真前缀与真后缀的长度。
用式子表示如下:\(\pi_i = \max_{k = 0 \cdots i} \{k : s[0 \cdots k - 1] = s[i - (k - 1) \cdots i] \}\)
1.1.2 朴素求解
比较显然的 \(O(n ^ 3)\),很明显是可以优化的。
bool check(int l1, int r1, int l2, int r2) {
for (int i = l1, j = l2; i <= r1, j <= r2; i++, j++)
if (s[i] != s[j])
return 0;
return 1;
}
for (int i = 2; i <= n; i++)
for (int j = i; j >= 1; j--)
if (check(1, j, i - j + 1, i)) {
pi[i] = j;
break;
}
1.1.3 优化一
我们通过观察 \(\pi_i\) 的定义可以发现 \(\pi_i \leq \pi_{i - 1} + 1\),比较显然,此时时间复杂度已降到 \(O(n ^ 2)\)。
bool check(int l1, int r1, int l2, int r2) {
for (int i = l1, j = l2; i <= r1, j <= r2; i++, j++)
if (s[i] != s[j])
return 0;
return 1;
}
for (int i = 2; i <= n; i++)
for (int j = pi[i - 1] + 1; j >= 1; j--)
if (check(1, j, i - j + 1, i)) {
pi[i] = j;
break;
}
1.1.4 优化二
假设我们已经求出了 \(\pi_1\) ~ \(\pi_p\),我们需要求 \(\pi_{p + 1}\)
-
首先令 \(i \leftarrow \pi_p\),这表示 s[1 \(\cdots\) p + 1] 的最长相等前缀后缀一定由 s[1 \(\cdots\) p],的相等前缀后缀扩展而来,我们先尝试最长的那个相等前缀后缀。
-
尝试匹配 \(s_{i + 1}\) 和 \(s_{p + 1}\),若相等,则得到 \(\pi_{p + 1} = i + 1\)。
-
若不满足条件,我们找次长的相等前缀后缀,也就是 \(\pi_i\),再回到上一步匹配后面一个字符。
此时我们的时间复杂度变为了 \(O(n)\)
for (int i = 2, p = 0; i <= n; i++) {
while (p && s[p + 1] != s[i]) p = pi[p];
pi[i] = p += s[p + 1] == s[i];
}
1.2 应用
1.2.1 Knuth-Morris-Pratt 算法
for (int i = 2, p = 0; i <= m; i++) {
while (p && t[p + 1] != t[i]) p = nxt[p];
nxt[i] = p += t[p + 1] == t[i];
}
for (int i = 1, p = 0; i <= n; i++) {
while (p && t[p + 1] != s[i]) p = nxt[p];
if (t[p + 1] == s[i]) p++;
if (p == m) cout << i - m + 1 << '\n', p = nxt[p];
}
1.2.2 字符串的周期
对字符串 s 和 0 < p \le |s|,若 s[i] = s[i+p] 对所有 i \in [0, |s| - p - 1] 成立,则称 p 是 s 的周期。
对字符串 s 和 0 \le r < |s|,若 s 长度为 r 的前缀和长度为 r 的后缀相等,就称 s 长度为 r 的前缀是 s 的 border。
由 s 有长度为 r 的 border 可以推导出 |s|-r 是 s 的周期。
根据前缀函数的定义,可以得到 s 所有的 border 长度,即 \pi[n-1],\pi[\pi[n-1]-1], \ldots。2
所以根据前缀函数可以在 O(n) 的时间内计算出 s 所有的周期。其中,由于 \pi[n-1] 是 s 最长 border 的长度,所以 n - \pi[n-1] 是 s 的最小周期。
1.2.4 KMP 自动机
本文来自博客园,作者:zhou_ziyi,转载请注明原文链接:https://www.cnblogs.com/zhouziyi/p/string.html