扩展 KMP(Z 函数)
【模板】扩展 KMP(Z 函数)
前言
做这题时我还不会扩展 KMP,看了奆佬
现在我已取得了奆佬的授权,于是本篇题解将会结合这位奆佬的笔记与我自己的理解和绘图来展现给大家,如有错漏敬请指出!
希望这篇题解能对你有帮助 ヾ(◍ ╹ ∇ ╹ ◍)ノ゙!
修正
2021.07.12 感谢
2021.07.26 感谢
2022.05.12 感谢
2022.11.05 感谢 可恶的博客竟然无法正常显示表格边框,可恶,已将例子换为图片!
扩展 KMP
扩展 KMP(exKMP),能在
我会分两部分来讲解 exKMP,分别为
nxt(next)
类似于 KMP,exKMP 也需要一个
举个例子 :
我们先不管
假设
对于所有的
细心的你一定会发现我们没有算入
根据以上定义我们可以得到,在模式串
现在我们要求模式串
前面我们知道,
我们定义
此时,如下图所示,如果图中的灰方框小于初始的绿方框,也就是
否则,如下图所示,灰方框大于初始的绿方框。此时的
因为
那么我们现在就能写出求
void qnxt(char *c)
{
int len = strlen(c);
int p = 0, k = 1, l; //我们会在后面先逐位比较出 nxt[1] 的值,这里先设 k 为 1
//如果 k = 0,p 就会锁定在 |c| 不会被更改,无法达成算法优化的效果啦
nxt[0] = len; //以 c[0] 开始的后缀就是 c 本身,最长公共前缀自然为 |c|
while(p + 1 < len && c[p] == c[p + 1]) p++;
nxt[1] = p; //先逐位比较出 nxt[1] 的值
for(int i = 2; i < len; i++)
{
p = k + nxt[k] - 1; //定义
l = nxt[i - k]; //定义
if(i + l <= p) nxt[i] = l; //如果灰方框小于初始的绿方框,直接确定 nxt[i] 的值
else
{
int j = max(0, p - i + 1);
while(i + j < len && c[i + j] == c[j]) j++; //否则进行逐位比较
nxt[i] = j;
k = i; //此时的 x + nxt[x] - 1 一定刷新了最大值,于是我们要重新赋值 k
}
}
}
ext(extend)
我们用
假设
跟上文求
根据
void exkmp(char *a, char *b)
{
int la = strlen(a), lb = strlen(b);
int p = 0, k = 0, l;
while(p < la && p < lb && a[p] == b[p]) p++; //先算出初值用于递推
ext[0] = p;
for(int i = 1; i < la; i++) //下面都是一样的逻辑啦
{
p = k + ext[k] - 1;
l = nxt[i - k];
if(i + l <= p) ext[i] = l;
else
{
int j = max(0, p - i + 1);
while(i + j < la && j < lb && a[i + j] == b[j]) j++;
ext[i] = j;
k = i;
}
}
}
代码
在处理完
我的代码里下标是从
下面放出完整代码:
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 2e7 + 10;
ll nxt[N], ext[N];
void qnxt(char *c)
{
int len = strlen(c);
int p = 0, k = 1, l; //我们会在后面先逐位比较出 nxt[1] 的值,这里先设 k 为 1
//如果 k = 0,p 就会锁定在 |c| 不会被更改,无法达成算法优化的效果啦
nxt[0] = len; //以 c[0] 开始的后缀就是 c 本身,最长公共前缀自然为 |c|
while(p + 1 < len && c[p] == c[p + 1]) p++;
nxt[1] = p; //先逐位比较出 nxt[1] 的值
for(int i = 2; i < len; i++)
{
p = k + nxt[k] - 1; //定义
l = nxt[i - k]; //定义
if(i + l <= p) nxt[i] = l; //如果灰方框小于初始的绿方框,直接确定 nxt[i] 的值
else
{
int j = max(0, p - i + 1);
while(i + j < len && c[i + j] == c[j]) j++; //否则进行逐位比较
nxt[i] = j;
k = i; //此时的 x + nxt[x] - 1 一定刷新了最大值,于是我们要重新赋值 k
}
}
}
void exkmp(char *a, char *b)
{
int la = strlen(a), lb = strlen(b);
int p = 0, k = 0, l;
while(p < la && p < lb && a[p] == b[p]) p++; //先算出初值用于递推
ext[0] = p;
for(int i = 1; i < la; i++) //下面都是一样的逻辑啦
{
p = k + ext[k] - 1;
l = nxt[i - k];
if(i + l <= p) ext[i] = l;
else
{
int j = max(0, p - i + 1);
while(i + j < la && j < lb && a[i + j] == b[j]) j++;
ext[i] = j;
k = i;
}
}
}
int la, lb;
char a[N], b[N];
ll ans;
int main()
{
cin.tie(0); cout.tie(0);
ios::sync_with_stdio(0);
cin >> a >> b;
qnxt(b);
exkmp(a, b);
la = strlen(a), lb = strlen(b), ans = 0;
for(int i = 0; i < lb; i++) //要注意下标从 0 开始
{
ans ^= (i + 1) * (nxt[i] + 1);
}
cout << ans << "\n";
ans = 0;
for(int i = 0; i < la; i++)
{
ans ^= (i + 1) * (ext[i] + 1);
}
cout << ans;
return 0;
}
后记
感谢你的阅读,希望这篇题解能对你有帮助!
加油吧!✧*٩( ◕ ᗜ ◕ )و✧*