文章标题 改进的模式匹配算法

一.算法功能:
改进的模式算法是由Knuth,Morris和Pratt等人共同提出的,所以称为Knuth-Morris-Pratt算法,简称KMP算法。KMP算法是字符串模式匹配中的经典算法,在起匹配过程中,主串指针不回溯,从而提高了算法性能。

二.算法思想

  1. 在匹配过程中,如果出现不匹配的情况(当前模式串不匹配字符假设为t[i]),首先从已匹配结果计算出目标串S的第i个字符应该与模式串T中哪个字符在比较时出现了不配的情况,即保证在目标串指针不回溯的前提下,确定模式串中新的比较起点。
      设主串是S=’S1S2.....Sn',模式串为T=T1T2.....Tm;
  2.根据模式串T自身的规律和已知当前的位置j,可以归纳出计算模式串新的比较起点k的表达式。令k=next[j];
           next[j]=0  (当j=1)
           next[j]=max{k|1<k<j且'T1T2.....Tk-1'='Tj-k+1Tj-k+2......Tj-1';
           next[j]=1; 其他情况
           需要说明的是,next[j]中next[0]和next[1]的取值是固定的。
           *匹配规则:*
           (1)当首字母出现不匹配的时候,目标串的指针后移一位,然后在从改位与模式串的第一个字符开始匹配。假定next[0]=-1;
           (2)失配位置j所对应的next[j]的值为接下来要匹配的模式串的字符的索引,也就是说,出现不匹配的时候,模式串的索引指针要回溯到next[j]所对应的位置,而目标串索引指针不回溯。
  3.举个例子说明:
      例如,现有目标串S='cabdabaabcabaabadcb',模式串T='abaaba'
    1).先把模式串T中可能的失配点j多对应的next[j]计算出来(*由上面的next[j]计算公式*)

      j=1时,next[1]=0;
      j=2时,next[2]=1;
      j=3时,首先,1<k<j,所以k=2;   但是'T1'(此处T1为a)不等于'T2'(此处T2为b),所以next[3]=1;
      j=4时,k={2,3},k=2时,'T1'='T3'(此处T1等于a,T2等于a)
                                 k=3时,  'T1T2'不等于'T2T3'(此处T1T2是ab,T2T3是ba)
                          因此next[4]=max{k|1<k<j且 'T1T2.....Tk-1'='Tj-k+1Tj-k+2......Tj-1'}=2;
      以此类推
      j=5时,next[5]=2;
      j=6时,next[6]=3;

   2).计算完next[j]的值,接下来开始匹配
       *第一次匹配*:S='*c*abdabaabcabaabadcb'
                          T='*a*baaba'
                 设两个参数i和j,i代表目标串的指针索引位置,j代表模式串指针索引位置,开始时,i=1,j=1;  第一个字符都不匹配,next[j]=next[1]=0;
          *第二趟匹配*:目标串指针加1,i=2,j=1;
                            S='c*abd*abaabcabaabadcb'
                            T='*aba*aba'
                            匹配失败时,i=4,j=3;next[j]=1(当j=3时)
          *第三趟匹配*:目标串指针不变,j=1.就是从i=4,j=1开始匹配(此处的原因是上文中的匹配规则)
                            S='cab*d*abaabcabaabadcb'
                            T='*a*baaba'
                           匹配失败时i=4,j=1,next[j]=0(当j=1时)
         *第四次匹配*:目标串指针加1,模式串指针指向第一个字符,也就是j=1,从i=5,j=1开始匹配。
                           S='cabd*abaabc*abaabadcb'
                           T='*abaaba*'
                           失配时i=10,j=6,next[j]=3(j=6)
          *第五次匹配*:目标串不变,从i=10,j=3,开始匹配,
                            S='cabdabaab*c*abaabadcb'
                            T='ab*a*aba'
                            失配时,i=10,j=3,next[j]=1(j=3)
          *第六次匹配*:目标指针不变,从i=10,j=1,开始匹配,
                              S='cabdabaab*c*abaabadcb'
                              T='*a*baaba'
                               失配时i=10,j=1,next[j]=0;
           *第七次匹配*:目标指针加1,j=1,从i=11,j=1开始匹配
                              S='cabdabaabc*abaaba*dcb'
                              T='*abaaba*'  匹配成功,返回模式串在目标串中的位置11.
            以上匹配过程看起来挺麻烦的,只要一步步慢慢来,是很容易理解的;

三.下面是具体的代码实现:

#include<stdio.h>

#define MAXL 255
#define OK 1
#define OVERFLOW -1

typedef unsigned char SString[MAXL + 1];

void strAssign(SString &T, char *s)
//用字符数组s给串T赋值.
{
    int i = 0;
    T[0] = 0;//0号单元存储串长.
    for (; s[i]; i++)
    {
        T[i + 1] = s[i];
    }
    T[0] = i;
}

void get_next(SString T, int next[])
{
    //求模式串T的next函数值并存入数组nextint i = 1, j = 0;
    next[1] = 0;
    while (i < T[0])
    {
        if (j == 0 || T[i] == T[j])
        {
            i++; j++;
            next[i] = j;
        }
        else
            j = next[j];
    }
}

int Index_KMP(SString S, SString T)
{
    //求子串T在主串S中可以匹配的位置,不匹配返回0
    int i = 1, j = 1;
    int next[MAXL];
    get_next(T, next);
    while (i <= S[0] && j <= T[0])
    {
        if (j == 0 || S[i] == T[j])
        {
            i++;
            j++;
        }
        else
            j = next[j];
    }
    if (j > T[0]) return i - T[0];
    else return 0;
}

void main()
{
    int pos;
    SString T, S;
    char char_a[100], char_b[100];
    printf("请输入主串A:");
    gets_s(char_a);
    printf("%s\n", char_a);
    printf("请输入主串B:");
    gets_s(char_b);
    printf("%s\n", char_b);

    strAssign(T, char_a);
    strAssign(S, char_b);

    printf("赋值成功!\n");

    pos = Index_KMP(T, S);
    if (pos)
    {
        printf("主串 T=%s 的子串 S=%s 在第%d个位置开始匹配。", char_a, char_b, pos);
    }
    else
        printf("主串 T=%s 和子串 S=%s 不匹配", char_a, char_b);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值