KMP算法

KMP这个算法几个月前就听了好多遍也没有搞懂,就跳过了,现在老师要求让写,就又搞了好久,就现在而言,好像懂了,又好像有点迷糊。在老师讲过话又补充了一点,如果哪位大佬不小心看到了,发现第二块段代码有啥问题,一定要跟我讲哦,毕竟我很菜,自己搞得,也不知道对不对。

字符串匹配

BF暴力做法:

#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

int main()
{
    int n,m;
    char s[20],p[20];
    cin>>n>>s+1>>m>>p+1;
    //这个代码是最终如果没有匹配的就输出0,有匹配成功了就输出1
    //BF算法,暴力时间复杂度O(m*n)
    int t=1;
    for(int i=1;i<=n;i++)
    {
        t=1;
        for(int j=1;j<=m;j++)
        {
            if(s[i+j-1]!=p[j])
            {
                t=0;
                break;
            }
        }
        if(t==1) break;

    }
    if(t==1) cout<<1<<endl;
    else cout<<0<<endl;
    return 0;
}

KMP:

40c2584855594731adcbfdd2103be4f9.jpeg

 重点放到模式串P上

10.9今天我们老师又讲了kmp的ne数组的计算方法,按照他讲的来看一遍,这个ne应该是每次不匹配时直接把j这个指针挪到模式串P的哪个位置,但是这个求的ne好像只可以找到第一次出现的位置:

1.首先初使ne[1]=0,ne[2]=1;

2.接下来后面求解每一位的ne时,根据前一位进行比较;首先将前一位和它的ne对应的字符进行比较:

(1).如果相等,那么这一位(第i位)的ne就等于前一位(第i-1位)的ne+1:

        (比如当前求的是第三位P[3]的ne值,即想要求ne[3],那么你就看P[2],就是比较P[2]是否等于 于P[ne[2]],P[2]它的ne[2]=1,即看P[2]是否等于P[1],等于了那么ne[3]=ne[2]+1=2);

(2).如果不等,那么向前继续寻找ne对应的字符与前一位(第i-1位)的字符比较,直到找到了某一位(记作第m位,显然m一定小于i-1)的ne对应的字符与前一位(第i-1位)的字符相等,那么这一位的ne就等于ne[m]+1;如果第m位的ne等于1,然后P[1]还不等于前一位(第i-1位)的字符,那么ne[i]=1:

        (比如当前求的是第三位P[3]的ne值,即想要求ne[3],你发现P[2]!=P[ne[2]],就是P[2]!=P[1],那么ne[3]=1;)

下面一个例子,模式串P为 :abcaabbabcab(下标都是从1开始):

首先ne[1]=0,ne[2]=1;

ne[3]:P[2]='b',ne[2]=1,P[2]!=P[1],所以ne[3]=1;

ne[4]:P[3]='c',ne[3]=1,P[3]!=P[1],所以ne[4]=1;

ne[5]:P[4]='a',ne[4]=1,P[4]==P[1],所以ne[5]=ne[4]+1=2;

ne[6]:P[5]='a',ne[5]=2,P[5]!=P[ne[5]],所以你看P[ne[ne[5]]=P[1],发现这个时候P[1]==P[5]了,所以ne[6]=ne[ne[5]]+1,所以ne[6]=2;

ne[7]:P[6]='b',ne[6]=2,P[6]==P[ne[6]],所以ne[7]=ne[6]+2=3;

同理可继续往下算得到该例子P的ne数组的值为ne={0,1,1,1,2,2,3,1,2,3,4,5};

我太饿了,代码下午下课之后再写

我回来了!

上述逻辑代码如下:

#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

int main()
{
    int n,m;
    char s[20],p[20];
    cin>>n>>s+1>>m>>p+1;
   
    //求ne
    int ne[20];
    memset(ne,0,sizeof(ne));
    ne[1]=0;
    ne[2]=1;
    for(int i=3;i<=m;i++)
    {
        if(p[i-1]==p[ne[i-1]]) ne[i]=ne[i-1]+1;
        else
        {
            int j=ne[i-1];
            while(j!=0 && p[i-1]!=p[j]) j=ne[j];
            j+=1;
            ne[i]=j;
        }
    }
    
    //for(int i=1;i<=m;i++) cout<<ne[i]<<endl;
    for(int i=1,j=1;i<=n;i++)
    {
        while(j!=0 && s[i]!=p[j]) j=ne[j];
        if(j==0) j=1;
        if(s[i]==p[j]) j++;
        if(j-1==m)
        {
            cout<<i-m+1<<endl;//输出模式串在主串的位置
            j=ne[j-1]+1;
        }
    }

    return 0;
}

求nextval数组值

求nextval数组值有两种方法,一种是不依赖next数组值直接用观察法求得,一种方法是根据next数组值进行推理,两种方法均可使用,视更喜欢哪种方法而定。

例如主串为“aaabaaaab”、

模式串为“aaaab”

1.第一位的nextval值必定为0,第二位如果与第一位相同则为0,如果不同则为1。

2.第三位的next值为1,那么将第三位和第一位进行比较,均为a,相同,则,第三位的nextval值为0。

3.第四位的next值为2,那么将第四位和第二位进行比较,不同,则第四位的nextval值为其next值,为2。

4.第五位的next值为2,那么将第五位和第二位进行比较,相同,第二位的next值为1,则继续将第二位与第一位进行比较,不同,则第五位的nextval值为第二位的next值,为1。

5.第六位的next值为3,那么将第六位和第三位进行比较,不同,则第六位的nextval值为其next值,为3。

6.第七位的next值为1,那么将第七位和第一位进行比较,相同,则第七位的nextval值为0。

7.第八位的next值为2,那么将第八位和第二位进行比较,不同,则第八位的nextval值为其next值,为2。

y总讲的是:

ne数组的概念就是:比如ne[i]=j就是以i为终点的后缀和以1为起点的前缀的字符串相等的最大长度,就是每次发生不匹配时,模式串移动的距离
比如字符串p:abcdabc,则ne[1]=0,ne[2]=0,ne[3]=0,ne[4]=0,ne[5]=0,ne[6]=2,ne[7]=3; 显然以s[1]为后缀的长度为0;
以s[2]=b为终点的后缀有{b},以1为起点的前缀有{a},相等的最大长度为0;
对于ne[6]=2来说,以s[6]=b为终点的后缀有{b,ab,dab,cdab,bcdab},以1为起点的前缀有{a,ab,abc,abcd,abcda},发现前缀等于后缀的最大长度是2
    
//问题就在如何计算ne数组,事实上它就是模式串自己与自己匹配的一个过程。

(为什么每次j都从0开始呢,因为对于主串s和模式串p的比较,每次p中试图与s[i]比较的是p[j+1],所以对j总是向前错一位)

总代码如下:(yxc)

#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

int main()
{
    int n,m;
    char s[20],p[20];
    cin>>n>>s+1>>m>>p+1;

    //KMP算法,下面是字符串下标以1开始
    int ne[200];
    memset(ne,0,sizeof(ne));
    for(int i=2,j=0;i<=m;i++)
    {
        while(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)
        {
            cout<<i-m+1<<' ';
            j=ne[j];
        }
    }


    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值