模式匹配(第二篇KMP算法

来源:互联网 发布:linux 网卡网关 编辑:程序博客网 时间:2024/06/05 14:28

  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 100typedef 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个位置插入串s2SqString 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;}//输出串strvoid 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

1 0
原创粉丝点击