EVERYTHING HAPPENS FOR THE BE|

wnsyou

园龄:2年4个月粉丝:19关注:16

2023-10-08 03:37阅读: 26评论: 0推荐: 0

字符串基础与 KMP

OI-wiki Link

要提到 KMP 算法,首先得提到字符串相关知识。

字符串相关

概念

  • 前缀/后缀:这个很容易理解。
  • 真前缀/真后缀:就是非原串的前缀/后缀。
  • 子串:从原串中选取连续的一段就是一个子串,空串也算子串。
    • 任何子串都是一个前缀的后缀/一个后缀的前缀。
  • 周期:当满足 si=si+x(1i|s|x) 时,xs 的周期。
  • Border:当一个字符串 t,既是 s 的前缀,又是 s 的后缀时,t 就是 s 的一个 Border。

性质

  • 当一个字符串 ts 的 Border 时,|s||t|s 的一个周期。
  • Border 具有传递性,即当 xy 的 Border、yz 的 Border 时,必然有 xz 的 Border。
  • Border 传递性 2:当 xz 的 Border、yz 的 Border 时,必然有 xy 的 Border。

字符串匹配

模板:P3375 【模板】KMP

pre(s,i) 表示 s 的长度为 i 的前缀,suf(s,i) 表示 s 的长度为 i 的后缀。

给定两个字符串 st(1|s|,|t|105),现在要查询 st 中的出现位置有哪些。

暴力匹配 O(|s|×|t|),爆炸。

我们拿两个指针 i,j 表示 s[1i]t[ji+1j] 完全相同,j1|t| 循环,同时 i 相应变化,始终满足 KMP 性质:suf(pre(t,j),i)=pre(s,i)(i 越大越好)。

在更新时 i,j 时:

  • si+1=tj+1,则各自后移,i++, j++;,当 i=n 时,s 已经完全匹配,可以推出它的起始位置等。
  • 否则,右移 j,调整 i,使得 suf(pre(t,j),i)=pre(s,i) 仍然满足,那么该如何调整呢?

Next[] 失配数组

在发生不匹配时,我们需要调整 i,这个可以通过预处理来解决,通常定义为 Next 数组,有时也叫 fail 数组(失配数组)。

nxt[i] = max{k | pre(s, k) = suf(pre(s, i), k)},即 pre(s,i) 的最长 Border。

pre(s,k)pre(s,i) 的 Border,则有:

  • pre(s,k1)pre(s,i1) 的 Border。
  • sk=si

求解方法

假设 pre(s,i1) 的所有 Border 长度为 k1>k2>k3

  • 需要找到其中最大的 k 使得 sk+1=si
  • 此时 nxt[i] = k + 1(即 pre(s,i) 的最长 Border)。

根据定义和 Border 的传递性,pre(s,i1) 的所有 Border 其实就是 nxti1,nxtnxti1

  • nxti 就是 k=nxti1 开始检查 sk+1=sj是否成立,不成立就一直往前找 Next。k=nxtk 然后重复上述判断(找到满足条件的最长 Border)。

Code

void get_fail () {
nxt[0] = -1;
for (int i = 2, j = 0; i <= m; i++) {
while (j >= 0 && t[i] != t[j + 1]) j = nxt[j];
nxt[i] = ++j;
}
}

求完了失配数组,剩下就好办了。

KMP Code

for (int i = 1, j = 0; i <= n; i++) {
while (j >= 0 && s[i] != t[j + 1]) j = nxt[j];
j++;
if (j == m) {
cout << i - j + 1 << '\n';
}
}

模板完整代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
string s, t;
int n, m, nxt[N];
void get_fail () {
nxt[0] = -1;
for (int i = 2, j = 0; i <= m; i++) {
while (j >= 0 && t[i] != t[j + 1]) j = nxt[j];
nxt[i] = ++j;
}
}
int main () {
ios::sync_with_stdio(0), cin.tie(0);
cin >> s >> t, n = s.size(), m = t.size(), s = " " + s, t = " " + t;
get_fail();
for (int i = 1, j = 0; i <= n; i++) {
while (j >= 0 && s[i] != t[j + 1]) j = nxt[j];
j++;
if (j == m) {
cout << i - j + 1 << '\n';
}
}
for (int i = 1; i <= m; i++) {
cout << nxt[i] << ' ';
}
return 0;
}

时间复杂度:O(|s|+|t|)

字符串的周期

性质里有,而字符串 s 的最小周期则是 |s|nxt|s|

失配树

顾名思义,就是将 i(1in) 连向 nxti 所形成的树。

这棵树有什么用呢?树上的两个节点 x,y 的 LCA 就是 pre(s,x)pre(s,y) 的最长公共 Border。

而一个节点 i 的祖先则都是 pre(s,i) 的 Border。

例题:P5829 【模板】失配树 | P3435 [POI2006] OKR-Periods of Words

本文作者:wnsyou の blog

本文链接:https://www.cnblogs.com/wnsyou-blog/p/KMP.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   wnsyou  阅读(26)  评论(0编辑  收藏  举报
  1. 1 勝利への道 安藤浩和
  2. 2 Minecraft’s End Eric Fullerton
  3. 3 月光曲完整版 贝多芬 云熙音乐
  4. 4 平凡之路 (Live版) 朴树
  5. 5 Minecraft C418
  6. 6 Paradise NiziU
  7. 7 叫我,灰原哀 龙大人不喷火
  8. 8 心机之蛙,一直摸你肚子 ——《名侦探柯南》原创同人曲 炊饭,叶辞樱,温海,寒砧,南柯柯,小茜玛姬,盛姝,阿崔Ac,贝壳初,千湛,兮茶子DaYu,乔慕,黎鹿北,起千温卿,遮阳伞,曲悠
  9. 9 战 歌 此去经年
月光曲完整版 贝多芬 - 云熙音乐
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

暂无歌词

加载中…

{{tag.name}}

{{tran.text}}{{tran.sub}}
无对应文字
有可能是
{{input}}
尚未录入,我来提交对应文字
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示