KMP如何从入门到入土

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,还不想自己写博客,我果然是一个神奇的蒟蒻。我好菜。我自己下去看看优化吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值