KMP板子
-
教程
传送门 1 -
复杂度
对于模式串 t 和目标串 t 都进行了一次扫描,所以复杂度为 O (m + n)
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
const int MS = 1e5;
#define ll long long
void Getnext(int next[], string t)
{
int j = 0, k = -1;
next[0] = -1;
while(j < t.size() - 1)
{
if(k == -1 || t[j] == t[k])
{
j ++, k ++;
if(t[j] == t[k]) //如果j、k 位置的字符相同,那么如果j位置的字符与 “目标串” 中的字符不匹配,
// 那么我们让“模式串” 向右滑动重新匹配(1⃣️ 在KMP函数中令 j = next[j])的时候,
//在即将要判断的k位置的字符也一定与当前正在匹配的字符不一样,所以此时又要循
//环右滑(2⃣️ 再次令 j = next[j]), 而我么可以在Getnext函数中一步到位这个过程:
// next[j] = next[k] ==> 等价于:next[j] = k && k = next[k](这一步是因为k位置一定
//不想等(匹配)产生出来的),所以这我们得到的最终表达式,只是一个推导的产物
next[j] = next[k];
else
next[j] = k;
}
else k = next[k];
}
}
int KMP(string s, string t) //s主串,t模式串
{
int next[MS], i = 0, j = 0;
Getnext(next, t);
while(i < s.size() && j < (int)t.size()) // 注意:这里的 j 是int类型,被强制转化为 usigned long 会变成一个超大的负数,所以 这里的如果 j = -1 时 如过被强制转换成 usigned long 会if语句不成立,但是原来是成立的
{
if(j == -1 || s[i] == t[j])
i ++, j ++;
else
j = next[j];
}
if(j == t.size())
return i - (int)t.size() + 1;
else
return -1;
}
int main()
{
/* freopen("A.txt","r",stdin); */
/* freopen("Ans.txt","w",stdout); */
string s, t;
s = "abacda";
t = "abcabcdabcabcda";
int next[t.size()];
Getnext(next , t);
for(int i = 0; i <= t.size() - 1; i ++)
cout << next[i] << endl;
return 0;
}
可重复匹配简短模板
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, m;
char s[N], p[N];
int ne[N];
int main()
{
scanf("%d %s %d %s", &m, p + 1, &n, s + 1); //下标从 1 开始 p 为模式串,s 为主串
//求 next 数组
for (int i = 2, j = 0; i <= m; i ++)
{
if (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j ++;
ne[i] = j;
}
for (int i = 1, j = 0; i <= n; i ++)
{
while (j && s[i] == p[j + 1]) j = ne[j];
if (s[i] == p[j + 1]) j ++;
if (j == m)
{
printf("%d ", i - m); //下标从 0 开始的所有,匹配位置
j = ne[j]; //可重复匹配
}
}
return 0;
}
从下表 1 位置开始的kmp匹配
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MXLEN 255 //最大串长
typedef char String [MXLEN + 1];
// T[0]、S[0] 中存储的是 T、S中的从下表 1 开始的数组的长度
int next[MXLEN];
void get_next(String T, int next[])
{
int j = 1, k = 0;
next[1] = 0;
while (j < T[0])
{
if (k == 0 || T[k] == T[j])
{
++j; ++k;
if (T[j] != T[k]) next[j] = k;
else next[j] = next[k];
}
else k = next[k];
}
}
int KMP(String S, String T, int pos)
{
int i = pos, j = 1;
while (i <= S[0] && j <= T[0])
{
if (j == 0 || S[i] == T[j]) { ++i; ++j; }
else j = next[j];
}
if (j > T[0]) return (i - T[0]);
else return 0;
}
多次重叠匹配
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, m;
char s[N], t[N];
int nxt[N], mk[N];
void get_nxt()
{
int j = 0, k = -1;
nxt[0] = -1;
while (j < m)
{
if (k == -1 || t[j] == t[k])
{
j ++, k ++;
if (t[j] == t[k])
nxt[j] = nxt[k];
else
nxt[j] = k;
}
else
k = nxt[k];
}
}
void kmp() //kmp 多次重叠匹配
{
int i = 0, j = 0;
while (i < n && j < m)
{
if (j == -1 || s[i] == t[j])
i ++, j ++;
else
j = nxt[j];
if (j == m)
{
mk[i - 1] = 1; //标记每个匹配串的结尾
j = nxt[j]; //根据多重匹配的性质来跳的!!!
}
}
}
void init()
{
memset(mk, 0, sizeof mk);
memset(nxt, 0, sizeof nxt);
}
int main()
{
init();
scanf("%s %s", s, t);
n = strlen(s);
m = strlen(t);
get_nxt();
kmp();
return 0;
}
exKMP
aaaaabbb
aaaaac
next: 6 4 3 2 1 0
extend: 5 4 3 2 1 0 0 0
abc
def
next: 3 0 0
extend: 0 0 0
exKMP代码
#include<iostream>
#include<string>
using namespace std;
const int mxn = 1e5;
int nxt[mxn];
int ext[mxn];
void Get_next(string T, int m)
{
int a = 0, p = 0;
nxt[0] = m;
for(int i = 1; i < m; i ++)
{
if(i >= p || i + nxt[i - a] >= p)
{
if(i > p) p = i;
while(p < m && T[p] == T[p - i]) p ++;
nxt[i] = p - i;
a = i;
}
else
nxt[i] = nxt[i - a];
}
}
void Get_ext(string S, int n, string T, int m)
{
int a = 0, p = 0;
Get_next(T, m);
for(int i = 0; i < n; i ++)
{
if(i >= p || i + nxt[i - a] >= p)
{
if(i > p) p = i;
while(p < n && S[p] == T[p - i]) p ++;
ext[i] = p - i;
a = i;
}
else
ext[i] = nxt[i - a];
}
}
int main()
{
string s, t;
cin >> s >> t;
int n = s.size();
int m = t.size();
Get_ext(s, n, t, m);
for(int i = 0; i < m; i ++)
{
cout << nxt[i] << " ";
}
cout << endl;
for(int i = 0; i < n; i ++)
{
cout << ext[i] << " ";
}
cout << endl;
return 0;
}