2022.6.2串的模式匹配KMP

KMP算法使得主串(目标串)指针不必回溯而只需回溯子串(模式串)指针,并且子串指针也不一定需要回溯到子串的第一个字符。算法的时间复杂度是O(m+n)

【问题描述】KMP算法是字符串模式匹配算法中较为高效的算法之一,其在某次子串匹配母串失败时并未回溯母串的指针而是将子串的指针移动到相应的位置。

【输入形式】3组字符串,每组字符串占一行。每行包含由空格分隔的两个字符串,字符串仅由英文小写字母组成且长度不大于100。

【输出形式】每组数据输出1行,输出后一个字符串在前一个字符串中的位置,如果不匹配,则输出0。

【样例输入】

string str
thisisalongstring isa
nosubstring subt

【样例输出】

1

5

0

【提示】表示字符串的数据结构可以是字符数组或用串类实现。KMP算法调用很简单,但难的是理解算法的思想。掌握算法的思想才能说是掌握算法。

next数组的求解以及输出:

#include <iostream>
#include <cstring>
using namespace std;

#define Maxsize 1000

char b[Maxsize];int next[Maxsize];

void Operator_next()
{
    int lenb=strlen(b);
    next[0]=-1;           //循环内部会使用到这个-1
    next[1]=0;            //此处这条语句也是可以在循环内部进行赋值的
    for(int i=2;i<lenb;i++)
    {
        int k=next[i-1];
        if(b[k]==b[i-1]) next[i]=k+1;
        else
        {
            while(k!=-1)
            {
                k=next[k];
                if(k==-1)break;
                if(b[k]==b[i-1])
                {
                    next[i]=k+1;
                    break;
                }
            }
            if(k==-1)next[i]=0;
        }
    }
}

void Output()
{
    int lenb=strlen(b);
    for(int i=0;i<lenb;i++)cout<<next[i]<<" ";
}

int main()
{
        cin>>b;
        Operator_next();
        Output();
    return 0;
}

思路:一般是将next[0]赋值为-1,之后便是看中间部分next数组的求解过程。

假设现在遍历到的位置是数组b的第i个位置,并设next[i-1]=k,则是需要看b[k]的值与b[i-1]的值是否相同了:

(一)如果b[k]与b[i-1]是相同的,则next[i]=next[i-1]+1;

例如:看下面的测试数据1,当i=5时,也就是现在要求的是next[5](也就是next数组前面的值已经求出来了);那么这个时候就要看next[i-1]的值,其中next[i-1]=1,则令k=next[i-1]=1,用k将这个时候i-1所对应的next的值记录下来;那么之后就要b[k]和b[i-1]是否相同了,如果相同则意味着next[i]=k+1=next[i-1]+1.

(二)如果b[k]与b[i-1]并不相同的时候,(我刚开始就是想既然都不一样了,那么子串进行回溯的时候肯定就是回溯到0就可以了,也就是将next[i]赋值为0。但是运行了下面的测试数据二之后,我就发现不对了,也就是这种情况下并不是像想象中的那么简单的,是需要回溯next数组进行一些判断的)

即:当b[k]!=b[i-1]的时候,就是需要回看next[k]的,并将k的数值进行更新,也就是k=next[k],然后就是接着看b[k]和b[i-1]是否相同;如果仍然不相同,则是接着进行回看。

结束回看的条件要么是b[i]=b[k]了,将next[i]赋值为k+1;要么就是会看到了next[0](也就是k=-1)仍然没有使得b[k]=b[i-1],那么就将next[i]的值赋为0就可以了。

例如:看下面的测试数据2

(1)当i=4的时候,k=next[i-1]=1,但是b[k]!=b[i-1](也就是b[1]=b不等于b[3]=a)所以就令k=next[k]=0,然后就再次比较b[k]和b[i-1]结果发现b[k]=b[0]=a是等于b[i-1]=b[3]=a的,所以就停止回看,对next[i]进行赋值,next[i]=k+1=1

(2)当i=6的时候,k=next[i-1]=2,但是b[k]!=b[i-1](b[k]=b[2]=a,而b[i-1]=b[5]=c,两者并不相等)所以就要开始进行回看;令k=next[k]=next[2]=0,然后就看b[k]和b[i-1]是否相同,及结果发现b[k]=b[0]=a,b[i-1]=b[5]=c,两者并不相同,然后就是接着回看;令k=next[k]=-1,发现k=-1,就停止回看,将next[i]的值赋为0

几组next数组的测试数据:

测试数据1
子串baabaac
next-101012
测试数据2
子串babaabcac
next-10011201
测试数据3
子串baabaabaabaac
next-101012345678

这道题的代码:

#include <iostream>
#include <cstring>
using namespace std;

#define Maxsize 1000

char a[Maxsize],b[Maxsize];int next[Maxsize];

void Operator_next()
{
    int lenb=strlen(b);
    next[0]=-1;
    next[1]=0;
    for(int i=2;i<lenb;i++)
    {
        int k=next[i-1];
        if(b[k]==b[i-1]) next[i]=k+1;
        else
        {
            while(k!=-1)
            {
                k=next[k];
                if(k==-1)break;
                if(b[k]==b[i-1])
                {
                    next[i]=k+1;
                    break;
                }
            }
            if(k==-1)next[i]=0;
        }
    }
}

int Match()
{
    int lena=strlen(a),lenb=strlen(b),xb=0,i=0,j=0,len=0,Max=0;
    while(i<lena&&j<lenb)
    {
        if(j==-1){i++;j=0;}
        else if(a[i]==b[j]){i++;j++;len++;}
        else
        {
            j=next[j];
            if(Max<len){xb=i;Max=len;}
            len=0;
        }
    }
    if(Max<len){xb=i;Max=len;}
    if(Max==lenb)return xb-Max+1;
    return 0;
}

int main()
{
    for(int i=0;i<3;i++)
    {
        cin>>a>>b;
        Operator_next();
        int xb=Match();
        cout<<xb<<endl;
    }
    return 0;
}

注:以上仅仅是作业提交通过的代码,如有其他错误或者是可以进一步优化的地方,欢迎指出

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值