EVERYTHING HAPPENS FOR T|

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 战 歌 此去经年
平凡之路 (Live版) - 朴树
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 朴树/韩寒

作曲 : 朴树

编曲 : 朴树

徘徊着的 在路上的

你要走吗 via via

易碎的 骄傲着

那也曾是我的模样

沸腾着的 不安着的

你要去哪 via via

谜一样的 沉默着的

故事你真的在听吗

我曾经跨过山和大海

也穿过人山人海

我曾经拥有着的一切

转眼都飘散如烟

我曾经失落失望失掉所有方向

直到看见平凡才是唯一的答案

当你仍然

还在幻想

你的明天 via via

她会好吗 还是更烂

对我而言是另一天

我曾经毁了我的一切

只想永远地离开

我曾经堕入无边黑暗

想挣扎无法自拔

我曾经像你像他像那野草野花

绝望着也渴望着

也哭也笑平凡着

jia do

la so

哦~

向前走 就这么走

就算你被给过什么

向前走 就这么走

就算你被夺走什么

向前走 就这么走

就算你会错过什么

向前走 就这么走

就算你会

我曾经跨过山和大海

也穿过人山人海

我曾经拥有着的一切

转眼都飘散如烟

我曾经失落失望失掉所有方向

直到看见平凡才是唯一的答案

我曾经毁了我的一切

只想永远地离开

我曾经堕入无边黑暗

想挣扎无法自拔

我曾经像你像他像那野草野花

绝望着 渴望着 也哭也笑平凡着

我曾经跨过山和大海

也穿过人山人海

我曾经问遍整个世界

从来没得到答案

我不过像你像他像那野草野花

冥冥中这是我 唯一要走的路啊

时间如烟

如此这般

明天已在 via via

风吹过的 路依然远

你的故事讲到了哪

加载中…

{{tag.name}}

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