模式匹配(第二篇KMP算法

53 篇文章 2 订阅
40 篇文章 1 订阅

  KMP算法是由D.E.Knuth、J.H.Morris和V.R.Pratt等人共同提出的,所以称为Knuth-Morris-Pratt算法,简称为KMP算法。KMP算法分析了模式串中隐藏的有利于模式匹配的信息。这种信息就是模式串中的“部分匹配”信息,或者,模式串中某个位置“前串等于后串”的最大长度信息。
  模式串t的next[j]数组公式如下:
   next[j]=Max{k|0<k<j,′′t0t1t′′k1=′′tjktjk+1t′′j1},1,0,j=0
  
  由此得到next[]数组的算法和KMP算法如下:  

//求next[]
void GetNext(SqString t,int next[]){
    int j,k;
    j=0; k=-1; next[0]=-1;
    while (j<t.length-1)
    {
        if (k==-1 || t.data[j]==t.data[k])
        {
            j++; k++;
            next[j]=k;
        }
        else k=next[k];
    }

}

//KMP算法:判断s与t是否匹配
int KMPIndex(SqString s,SqString t){
    int next[MaxSize],i=0,j=0,v;
    GetNext(t,next);
    while (i<s.length && j<t.length)
    {
        if(j==-1 || j<t.length){
            i++; j++;
        }
        else j=next[j];
    }
    if(j>=t.length) v=i-t.length;
    else v=-1;
    return v;

}

  设主串 s 的长度为n, 子串 t 长度为m, 在KMP算法中求next数组的时间复杂度为 O(m) , 在后面的匹配中因主串s的下标不减即不回溯,比较次数可记为n, 所以KMP算法总的时间复杂度为 O(n+m)
  上述算法,还可以改进一下,如下:  

//求NextVal[]数组
void GetNextVal(SqString t,int nextVal[]){
    int j=0,k=-1;
    nextVal[0]=-1;
    while(j<t.length){
        if (k==-1 || t.data[j]==t.data[k])
        {
            j++; k++;
            if(t.data[j]!=t.data[k]) nextVal[j]=k;
            else nextVal[j]=nextVal[k];
        }
        else k=nextVal[k];
    }
}

//修正后的KMP算法
int KMPIndex1(SqString s,SqString t){
    int nextval[MaxSize],i=0,j=0,v;
    GetNextVal(t,nextval);
    while (i<s.length && j<t.length)
    {
        if (j==-1 || j<t.length)
        {
            i++; j++;
        }
        else j=nextval[j];
    }
    if(j>=t.length) v=i-t.length;
    else v=-1;
    return v;

}

  与改进前的KMP算法一样,本算法的时间复杂度也为 O(n+m) 。虽然没有改变算法的数量级,但却减少了模式串与与主串的比较次数。
  《数据结构(c语言版)》–严蔚敏,把next[j]定义如下:
   next[j]=Max{k|1<k<j,′′t1t2t′′k1=′′tjk+1tjk+2t′′j1},0,1,j=1
  
  该公式与前面的公式是等价的,只不过next[j]定义的起点不同,前者j从0开始,next[0]=-1,next[1]=0,…; 后者j从1开始,next[1]=0,next[2]=1,…。如图(1)所示:

这里写图片描述

图(2)两种next[j]公式的表示情况,它们是等价的,只是起点定义的不同而已

完整代码如下:

//Base.h
#include <stdio.h>
#define  MaxSize 100
typedef struct{
    char data[MaxSize];
    int length;
}SqString;

//创建串
void StrAssign(SqString &str,char cstr[]){
    int i;
    for(i=0;cstr[i]!='\0';i++)
        str.data[i]=cstr[i];
    str.length=i;
}

//串复制
void StrCopy(SqString &s,SqString t){
    int i;
    for(i=0;i<t.length;i++)
        s.data[i]=t.data[i];
    s.length=t.length;
}

//判断串s与串t是否相同
int StrEqual(SqString s,SqString t){
    int same=1,i;
    if(s.length!=t.length)
        same=0;
    else{
        for(i=0;i<s.length;i++)
            if(s.data[i]!=t.data[i])
                same=0;
    }
    return same;

}

//求串长
int StrLength(SqString s){
    return s.length;
}

//两串比较
SqString Concat(SqString s,SqString t){
    SqString str;
    int i;
    str.length=s.length+t.length;
    for(i=0;i<s.length;i++)
        str.data[i]=s.data[i];
    for(i=0;i<t.length;i++)
        str.data[s.length+i]=t.data[i];
    return str;

}

//求串s中第i(1≤i≤n)个位置长度为j的子串   
SqString SubStr(SqString s,int i,int j){
    SqString str;
    int k;
    str.length=0;
    if (i<=0 || i>s.length || j<0 ||
        i+j-1 >s.length)
    {
        printf("参数不正确\n");
        return str;
    }
    for(k=i-1;k<i+j-1;k++)
        str.data[k-i+1]=s.data[k];
    str.length=j;
    return str;

}

//在串s的第i个位置插入串s2
SqString InsStr(SqString s1,int i,SqString s2){
    int j;
    SqString str;
    str.length=0;
    if(i<=0 || i>s1.length+1)  //参数不正确时,返回空串
    {
        printf("参数不正确\n");
        return s1;
    }
    for(j=0;j<i-1;j++)     //将s1.data[0]~s1.data[i-2]复制到str
        str.data[j]=s1.data[j];
    for (j=0;j<s2.length;j++)  //将s2.data[0]~s2.data[s2.length-1]复制到str
        str.data[i+j-1]=s2.data[j];
    for(j=i-1;j<s1.length;j++)  //将s1.data[i-1]~s.data[s1.length-1]复制到str
        str.data[s2.length+j]=s1.data[j];
    str.length=s1.length+s2.length;
    return str;

}

//删除串s中第i个位置长度为j的子串
SqString DelStr(SqString s,int i,int j){
    int k;
    SqString str;
    str.length=0;
    if (i<=0 || i>s.length || i+j>s.length+1)
    {
        printf("参数不正确\n");
        return str;
    }

    for(k=0;k<i-1;k++)
        str.data[k]=s.data[k];
    for (k=i+j-1;k<s.length;k++)
        str.data[k-j]=s.data[k];
    str.length=s.length-j;
    return str;

}

//在串s中,用串t替换在第i个位置长度为j的子串
SqString RepStr(SqString s,int i,int j,SqString t){
    int k;
    SqString str;
    str.length=0;
    if (i<=0 || i>s.length || i+j-1 >s.length)
    {
        printf("参数不正确\n");
        return str;
    }
    for(k=0;k<i-1;k++)
        str.data[k]=s.data[k];
    for (k=0;k<t.length;k++)
        str.data[i+k-1]=t.data[k];
    for(k=i+j-1;k<s.length;k++)
        str.data[t.length+k-j]=s.data[k];
    str.length=s.length-j+t.length;
    return str;

}

//输出串str
void DispStr(SqString str){
    int i;
    if (str.length>0)
    {
        for(i=0;i<str.length;i++)
            printf("%c",str.data[i]);
        printf("\n");
    }
}

//主函数.cpp
#include "base.h"
#include <stdio.h>

/*----------------------KMP算法-------------------------*/
//求next[]
void GetNext(SqString t,int next[]){
    int j,k;
    j=0; k=-1; next[0]=-1;
    while (j<t.length-1)
    {
        if (k==-1 || t.data[j]==t.data[k])
        {
            j++; k++;
            next[j]=k;
        }
        else k=next[k];
    }

}

//KMP算法:判断s与t是否匹配
int KMPIndex(SqString s,SqString t){
    int next[MaxSize],i=0,j=0,v;
    GetNext(t,next);
    while (i<s.length && j<t.length)
    {
        if(j==-1 || j<t.length){
            i++; j++;
        }
        else j=next[j];
    }
    if(j>=t.length) v=i-t.length;
    else v=-1;
    return v;

}

/*-------------------------改进的KMP算法---------------------*/
//求NextVal[]数组
void GetNextVal(SqString t,int nextVal[]){
    int j=0,k=-1;
    nextVal[0]=-1;
    while(j<t.length){
        if (k==-1 || t.data[j]==t.data[k])
        {
            j++; k++;
            if(t.data[j]!=t.data[k]) nextVal[j]=k;
            else nextVal[j]=nextVal[k];
        }
        else k=nextVal[k];
    }
}

//修正后的KMP算法
int KMPIndex1(SqString s,SqString t){
    int nextval[MaxSize],i=0,j=0,v;
    GetNextVal(t,nextval);
    while (i<s.length && j<t.length)
    {
        if (j==-1 || j<t.length)
        {
            i++; j++;
        }
        else j=nextval[j];
    }
    if(j>=t.length) v=i-t.length;
    else v=-1;
    return v;

}


void main()
{
    SqString str,str2;

    char s[20]="Helloworld";
    char s2[10]="llowo";
    StrAssign(str,s);
    StrAssign(str2,s2);
    //DispStr(str);

    printf("主串为: %s\n",s);
    printf("模式串为:%s\n",s2);

    int isPipei=0;
    int next[6];
//  GetNext(str2,next);
//  isPipei=KMPIndex(str,str2);

    GetNextVal(str2,next);
    isPipei=KMPIndex1(str,str2);

    if (isPipei != -1)
    {
        printf("匹配成功!\n");
    }
    else{
        printf("匹配失败!\n");
    }


}

效果如下:

这里写图片描述

模式匹配(第一篇:
http://blog.csdn.net/sanqima/article/details/48863425

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sanqima

一键三连,多多益善

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值