kmp详解

                         kmp详细讲解

字符串匹配算法有BF、RK、KMP、BM、Sunday!
BF大家都清楚,算法比较暴力,首先将原字符串和子串左端对齐,逐一比较;如果第一个字符不能匹配,则子串向后移动一位继续比较;如果第一个字符匹配,则继续比较后续字符,直至全部匹配。BF算法虽说解决了问题,但用时比较长。So咱们讲讲KMP。
KMP对萌新来说有点难度,不过慢慢来总会理解的。
下面我讲一下KMP匹配步骤:
在这里插入图片描述
在这里插入图片描述
很明显第一趟之后可以直接跳到第三趟,那该怎么实现呢?
咱们就引进一个next数组,它用于字串跳匹配的下标。比如上图我可以计算出主串的next数组是:-1,0,0,1,2。
大家就疑惑next数组怎么求,该怎么用,为什么可以这么用。
咱们先来讲讲求法吧:
这里咱们引入前缀与后缀的概念:
前缀是指除最后一个字符外,一个字符串全部头部组合;后缀是指除第一个字符外,一个字符串全部尾部组合。以”abcda”为例:
“ab”的前缀为[a],后缀为[b],共有元素的长度为0;
“abc”的前缀为[a, ab],后缀为[bc, c],共有元素的长度0;

“abcda”的前缀为[a, ab, abc, abcd],后缀为[bcda, cda, da, a],共有元素为”a”,长度为1;
而这个长度就是咱们的next值,第一个字符默认为-1。
咱们接下来讲讲用法吧:
依次匹配时,当字符不匹配,咱们直接讲主串下一个字符和子串该不匹配字符的next值作为下标对应的子串中的字符接着往下匹配。就如上两幅图所示,当第一躺第三个字符不匹配时,子串第三个字符next值为0,咱们直接跳到子串第一个字符(下标为0)和主串下一个字符匹配,也就是第三趟。
为什么会这样匹配呢:
咱们想想啊,next是最大前缀和后缀相同值,而匹配时前面的匹配对了,后缀与前缀有相同的,自然直接跳过,或许解释的有些抽象,但希望大家好好想想就应该能想明白。
Next求法(咱也不多说,直接上代码):

void getnext(char *t,int *next)
{
    int j=-1;
    int i=0;
    next[0]=-1;
    while(i<strlen(t))
    {
        if(j==-1||t[i]==t[j])//相等即匹配,i和j都往后移
        {
            i++;
            j++;
            next[i]=j;
        }
        //这里其实也相当于字符匹配,匹配不成功,j跳到next[j]
        else
        {
            j=next[j];
        }
    }
}

接下来是next使用,咱们就在主串中看是否有字串。

#include<iostream>
#include<string.h>
using namespace std;
void getnext(char *t,int *next)
{
    int j=-1;
    int i=0;
    next[0]=-1;
    while(i<strlen(t))
    {
        if(j==-1||t[i]==t[j])//相等即匹配,i和j都往后移
        {
            i++;
            j++;
            next[i]=j;
        }
        //这里其实也相当于字符匹配,匹配不成功,j跳到next[j]
        else
        {
            j=next[j];
        }
    }
}
int main()
{
    char *t1,*t2;
    int *next;
    t1=new char[10];
    t2=new char[10];
    next=new int[10];
    cin>>t1>>t2;
    getnext(t2,next);
    int i=0,j=0;
    int n1=strlen(t1),n2=strlen(t2);
    while(i<n1&&j<n2)//循环条件别写strlen(t1),否则只会判断一次
    {
        if(t1[i]==t2[j]||j==-1)
        {
            if(j==(strlen(t2)-1))
            {
                cout<<"yes"<<endl;
                return 0;
            }
            i++;
            j++;
        }
        else
        {
            j=next[j];
        }
    }
    cout<<"no"<<endl;
    return 0;
}

效果如下:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值