$KMP$ 算法

(KMP) 算法

(1.) 算法概述

给定长度为 (n) 的文本串 (text),以及长度为 (m) 的模式串 (pattern)

(KMP) 算法可以在 (O(m)) 时间内预处理出 (pattern) 的所有真前缀和真后缀中最大的公共长度

进而在 (O(n + m)) 时间内判断 (text) 中是否出现 (pattern),并求出 (pattern)(text) 中出现的所有位置

(2.) 算法详解

(2.1) 真后缀与真前缀

不包含串本身的后缀叫做真后缀

不包含串本身的前缀叫做真前缀

(2.2) 蛮力算法的局限性

蛮力算法的局限性在于,每次失配,都要重头再来匹配,如下图所示

(2.3) (KMP)

一个显然的优化如下

经过观察发现,这样的优化是基于如下事实

对于字符串 (abca),其所有真前缀和真后缀中最大的公共长度为 (1),相应的真前缀与真后缀均为 (a)

所以我们不必从头匹配,只需要将 (pattern[1] = a) 的后一项,即 (pattern[2] = b) 和模式串中的 (b) 继续匹配即可

更加普遍的,示例如下

如图所示,(text[i])(pattern[j + 1]) 失配,而绿色部分为相同的字符串,所以只需要将 (j) 指向 (j') 即可继续匹配

利用好真后缀与真前缀的信息,便是 (KMP) 的关键所在

代码如下

int L1 = strlen(text + 1);
for(int i = 1, j = 0; i <= L1; ++i) {
    while(j && text[i] != pattern[j + 1]) j = Next[j];
    if(text[i] == pattern[j + 1]) ++j;
    if(j == L2) printf("%d
", i - L2 + 1);
}

(2.4) (Next) 数组

(Next) 数组用于求解 (pattern) 的所有真前缀和真后缀中最大的公共长度

(pattern = abcabce),对应的 (Next) 数组示例如下

考虑求解 (Next) 数组

首先初始化 (Next[1]),令 (Next[1] = 0)

基于动态规划的思想,若 (pattern[i] = pattern[j+1]),那么 (Next[i] = Next[j] + 1)

(pattern[i] eq pattern[j]),只需要令 (j = next[j]) 即可

代码如下

int L2 = strlen(pattern + 1);
Next[1] = 0;
for(int i = 2, j = 0; i <= L2; ++i) {
    while(j && pattern[i] != pattern[j + 1]) j = Next[j];
    if(pattern[i] == pattern[j + 1]) Next[i] = ++j;
}

(3.) 代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int, int>
#define pb push_back
#define dbg(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " 
"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 11092019;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = 1e6 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
using namespace std;

inline LL read() //读入函数
{
    char c = getchar();
    LL ans = 0, f = 1;
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
    return ans * f;
}

char text[N], pattern[N];
int Next[N];
void KMP(char *text, char *pattern, int *Next)
{
    int L1 = strlen(text + 1), L2 = strlen(pattern + 1);
    Next[1] = 0;
    for(int i = 2, j = 0; i <= L2; ++i) {
        while(j && pattern[i] != pattern[j + 1]) j = Next[j];
        if(pattern[i] == pattern[j + 1]) Next[i] = ++j;
    }
    for(int i = 1, j = 0; i <= L1; ++i) {
        while(j && text[i] != pattern[j + 1]) j = Next[j];
        if(text[i] == pattern[j + 1]) ++j;
        if(j == L2) printf("%d
", i - L2 + 1);
    }
    dbg(Next, 1, L2);
}
int main()
{
    scanf("%s %s", text + 1, pattern + 1);
    KMP(text, pattern, Next);
    return 0;
}
原文地址:https://www.cnblogs.com/ChenyangXu/p/12920218.html