KMP从入门到安息最后再到入土
这个东西把我弄的头要秃了。
一、暴力匹配算法
说KMP之前先说一下暴力匹配算法,(这篇博客里面规定长串为被匹配串,短串为匹配串,求短串在长串的位置)所谓的暴力匹配算法,就是从短串第一个元素与长串中一相同元素匹配的地方开始匹配,如果没有匹配到短串的末尾并且出现了两个串的元素不相同的情况,就从短串之前匹配到的第一个元素的下一个元素开始匹配。
附上一串代码(自己临时起义写的,凑合着看吧
void bp(char t[],char s[]){
int len1=strlen(t),len2=strlen(s);
int i,j;
for(i=0,j=0;i<len1&&j<len2;){
if(t[i]==s[i]){
j++,i++;
}
else {
i=i-j+1;
j=0;
}
}
if(j==len2) {
return i-j+1;
}
else return -1;
}
二、KMP匹配
1.前缀和后缀。
讲KMP前,先说一下两个基本的概念:前缀和后缀。
前缀:指的是字符串的字串中从原串最前面开始的子串(这里的子串不包含它的本身),如abcdef的前缀有:a,ab,abc,abcd,abcde。
后缀:指的是字符串的子串中在原串结尾的子串,如abcdef的后缀有:f,ef,def,cdef,bcdef。
2.next数组。
KMP算法引进了一个next数组,next[i]表示的是前i的字符组成这个子串最长的相同前缀后缀的长度!!!
下面这个是从别人家的博客上面偷(读书人的事情,这是借)的数据,个人感觉这个数据非常好。
“abaabbabaab”
首先说一下next的求法:
1.next[0]=0,因为第一个字符没有字串。
2.如果s[0]!=s[1],next[1]=0;如果s[0]==s[1],next[1]=1;
3.next[2]和next[1]的求法一样,我就不多bb了。
4.next[3]:s[next[3-1]]=s[3]:next[3]=next[next[3-1]+1]+1;
那么,问题来了:这TMD到底是啥?我到底再看什么?这东西从哪里来?这是哪个傻逼写的?
首先我们要知道next数组代表是什么
我又扒了一段话(不是我懒的写,而是我不会(滑稽)):
我们求next[3]的时候,已经求出next[0]~next[2],此时我们知道了next[2]=1,意味着next[2]是代表着字符串的前缀与后缀相同的最大长度为1。
也就是对于ABA来说,前缀与后缀相同的最大长度为1,这是我们已知的条件,现在我们要知道对于ABAB来说,前缀与后缀相同的最大长度为??????
对于ABA来说,前缀"AB",后缀"BA",对于ABAB来说,前缀“ABA”,后缀“BAB”,现在我们观察得出结论
ABAB的前缀和后缀必然分别包含ABA的前缀和后缀,即next[3]字符串的前缀和后缀必然分别包含next[2]的前缀和后缀,
无论该字符串是何值。
既然我们已经知道了next[2]=1,代表着ABA的前缀与后缀相同的最大长度为1,那我们计算next[3]的时候,就知道了第一个字符必然是相同的,所以我们无需再比较第一个字符了,(KMP算法就是为了省这一步!!!!),所以我们将t[3]直接与t[1]而不是t[0]比较,若t[3]=t[1],那我们可以直接说next[3]=2=next[1]+1.
s[next[3-1]]!=s[3:怎么办?接下来是KMP的精髓:
void makeNext(char s[],int next[])
{
int len = strlen(s);
next[0]=0; //初始化
for(int i=1,k=0;i<len;i++)
{
while(k>0 && s[k]!=s[i]) //这个while是最关键的部分
k=next[k-1];
//等价于 k=next[next[i-1]-1]
//等号右边的k起的只是下标的作用
if(s[k]==s[i])
k++; //相等就+1
next[i]=k; //赋值
}
}
3.真·KMP求解
算了,KMP我不想写了,你们看这个链接,动图,绝对的通俗易懂
我觉得他的next数组写得不好,所以自己写了一下next,但中间有些很繁琐,还是扒了一个人的,真的是罪过。
最后放一个板子
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <cstring>
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
#include <iterator>
#define debug() puts("what the fuck")
#define mem(x,y) memset(x,y,sizeof(x))
using namespace std;
const int INF= 0x3f3f3f3f;
const int MAXN= 1e6+10;
double PI=3.1415926535898;
typedef long long ll;
void makeNext(char s[],int next[]){
int len = strlen(s);
next[0]=0;
for(int i=1,k=0;i<len;i++){
while(k>0&&s[k]!=s[i]){
k=next[k-1];
}
if(s[k]==s[i]){
k++;
}
next[i]=k;
}
}
int kmp(char t[],char s[]){
int len1=strlen(t),len2=strlen(s);
int next[len2];
makeNext(s,next);
for(int i=0,j=0;i<len1;i++){
while(j>0&&t[i]!=s[j]){
j=next[j-1];
}
if(t[i]==s[j])
j++;
if(j==len2){
return i-j+1;
}
}
}
int main(){
char s[200],t[200];
gets(t);
gets(s);
cout<<kmp(t,s);
return 0;
}
我果然不是一个干大事的人,我服了,好麻烦,以后博客还是写一些题解吧,为什么一个劲的想要写题解呢?因为,我颓废了两个月时间吧,现在被新生弄得不颓废了。大二了才学会KMP,还不想自己写博客,我果然是一个神奇的蒟蒻。我好菜。我自己下去看看优化吧。