KMP算法
刚开始学字符串算法的时候其实是十分抵触的,暴力匹配效率也不低嘛,但是在做题的时候才发现数据量真的很大,暴力还是炸了
所以还是乖乖的学字符串算法吧
先说几个字符串算法中的常用概念吧
文本串 :已有的长长的字符串(一般我写成T)
模式串 :要被查询的内容,就是说在文本串中要查找模式串(一般我写成P)(文本 串就是长的,模式串就是短的,要在文本串里找模式串,中华文化博大精深,不好描述,有二义性。。。)
然后看KMP算法吧:
KMP算法中文翻译是:字符串查找算法
顾名思义,就是一个 文本串 T 内查找一个 模式串 P 的出现位置。
当然,暴力算法就可以做但是效率太低,原因是一次匹配失败就要重新从模式串的头开始。。。效率低下,而KMP算法通过对这个词在不匹配时本身包含的信息来确定下一个匹配将在哪里开始的发现,从而避免重新检查先前匹配的字符,提高程序运行效率。
为了提高效率,KMP算法会在匹配之前预处理一个fail数组(有的地方写的是next数组,其实是一样的)借助 failfail 数组,可以在匹配过程中减少很多冗余的匹配操作,由此提高了算法的效率。KMP 算法的时间复杂度为O(n+m)。
首先看看fail数组
对于字符串 s=s0s1…sn−1,如果 j 是满足 s0…j=si−j…i 的最大值,则 faili=j,其中 sa…b 表示字符串 s 从下标 a 到下标 b 的子串。对于不存在 s0…j=si−j…i 的情况,fail[i]=−1。
例如,对于字符串aababaab,则 fail 值如下表所示:
S A A B A B A A B
fail −1 0 −1 0 −1 0 1 2
手算一下就知道了
我们先不管fail是这么算出来的,(当然和手算的算法不一样)
我们先学会怎么用,假设fail已经就出来了,我们开始匹配了
先给出伪代码
KMP(T, P)
fail = getFail(P)
match = -1
for i = 0 to T.length - 1
while match >= 0 and P[match + 1] != T[i]
match = fail[match]
if P[match + 1] == T[i]
match += 1
if match == P.length - 1
return true
return false
我们用两个值match和i来标记模式串 P 和匹配串 T 分别匹配到的下标位置,初始状态i=0,match=-1
会用之后给出fail数组的计算方法的伪代码,实际上和上面的伪代码一样可以理解为自己匹配自己
getFail(P)
m = P.length
fail[0] = -1
match = -1
for i = 1 to P.length - 1
while match >= 0 and P[match + 1] != P[i]
match = fail[match]
if P[match + 1] == P[i]
match += 1
fail[i] = match
return fail
好了,下面给出KMP算法的模板,一个是fail的一个是next的,个人习惯与第一个
fail数组的
void getFail(char *P, int *fail) {
int match = -1;
fail[0] = -1;
for (int i = 1; P[i]; ++i) {
while (match >= 0 && P[match + 1] != P[i]) {
match = fail[match];
}
if (P[match + 1] == P[i]) {
match++;
}
fail[i] = match;
}
}
bool KMP(char *T, char *P) { // T 是文本串,P 是匹配串
int fail[strlen(P)], match = -1; // 创建一个长度为 |P| 的数组 fail
getFail(P, fail);
for (int i = 0; T[i]; ++i) {
while (match >= 0 && P[match + 1] != T[i]) {
match = fail[match];
}
if (P[match + 1] == T[i]) {
match++;
if (!P[match + 1]) { // P[match + 1] == '\0',就意味着 match 是 P 串的最后一位下标,说明匹配成功
return true;
}
}
}
return false;
}
Next数组的
#include <iostream>
#include <string.h>
using namespace std;
int Next[1110];
void get_Next(char *p){
int m=strlen(p);
Next[0]=Next[1]=0;
for(int i=1;i<m;++i){
int j=Next[i];
while(j&&p[i]!=p[j]){
j=Next[j];
}
Next[i+1]=p[i]==p[j]?j+1:0;
}
}
int kmp(char *T,char *s){
int n=strlen(T),m=strlen(s);
get_Next(s);
int j=0;
for(int i=0;i<n;++i){
while(j&&T[i]!=s[j]){
j=Next[j];
}
if(s[j]==T[i]){
++j;
}
if(j==m){
return i-m+1;
}
}
return -1;
}
int main() {
char s[1100],T[1100];
cin>>T>>s;
cout<<kmp(T,s)<<endl;
return 0;
}