Appearance
字符串匹配:
设文本串为
当匹配到
cpp
void kmp(string &a, string &b) { // a 是文本串 b 是模式串,在 a 上找 b
int j = 0, n = a.size() - 1, m = b.size() - 1;
for (int i = 1; i <= n; i++) {
while (j && a[i] != b[j + 1])
j = border[j];
if (a[i] == b[j + 1])
j++;
if (j == m) {
// 匹配成功
j = border[j];
}
}
}时间复杂度和求 Border 类似,
扩展 Kmp(Z 函数):
对于一个字符串
对于
算法的过程中,维护右端点最靠右的匹配段。记作
在计算
- 如果
,那么根据 的定义有 ,因此 - 若
,则 。 - 否则
,这时令 ,然后暴力枚举下一位匹配。
- 若
- 如果
,那么直接从 开始暴力匹配。 - 求出
后,如果 ,更新 。
时间复杂度分析:
cpp
void init(string &s) { // 下标从 1 开始
int n = s.size() - 1;
for (int i = 2, l = 1, r = 1; i <= n; i++) {
if (i <= r && z[1 + i - l] < r - i + 1) {
z[i] = z[1 + i - l];
} else {
z[i] = max(0, r - i + 1);
while (i + z[i] <= n && s[1 + z[i]] == s[i + z[i]])
++z[i];
}
if (i + z[i] - 1 > r)
l = i, r = i + z[i] - 1;
}
z[1] = n;
}更一般地,利用
cpp
void exkmp(string &a, string &b) {
int n = a.size() - 1, m = b.size() - 1;
for (int i = 2, l = 1, r = 1; i <= n; i++) {
if (i <= r && z[1 + i - l] < r - i + 1) {
Z[i] = z[1 + i - l];
} else {
Z[i] = max(0, r - i + 1);
while (1 + Z[i] <= m && i + Z[i] <= n && b[1 + Z[i]] == a[i + Z[i]])
++Z[i];
}
if (i + Z[i] - 1 > r)
l = i, r = i + Z[i] - 1;
}
while (1 + Z[1] <= m && 1 + Z[1] <= n && b[1 + Z[1]] == a[1 + Z[1]])
++Z[1];
}Kmp 自动机 / Border 树(失配树):
对于一个字符串
- 前缀
的所有 Border:节点 到根的链。 - 有长度为
的 Border 的前缀: 的子树。 - 两个前缀
的公共 Border: 。
也可以认为是只有一个串的 AC 自动机。
