• KMP算法


    KMP算法可以再O(m+n)的时间数量级上完成串的模式匹配操作,对于普通的模式匹配的改进之处在于:每当一趟匹配过程中出现字符比较不等时,不需回溯指向主串的指针,而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远的一段距离后,继续比较。看下具体的例子:

                           i=3

    第一趟匹配   a b a b c a b c a c b a b

                     a b c

                           j=3

                                     i=7

    第二趟匹配   a b a b c a b c a c b a b

                          a b c a c

                                     j=5

                                               i=11

    第三趟匹配   a b a b c a b c a c b a b

                                (a) b c a c

                                     j=2      j=6

    设主串为‘s1s2...sn’,模式串为'p1p2...pm' 

    主要需要解决的问题是:当主串中第i个字符与模式中第j个字符“失配”时,主串中第i个字符(i 指针不回溯)应与模式中哪个字符再比较?

      假设此时应与模式中第k(k<j)个字符继续比较,则模式中前k-1个字符的子串必须满足下列关系式

      'p1p2...pk-1'='s(i-k+1)s(i-k+2)...s(i-1)'      (1)

       而已经得到的“部分匹配”的结果是

       'p(j-k+1)p(j-k+2)...p(j-1)'='s(i-k+1)s(i-k+2)...s(i-1)'   (2)

    由此推出下列等式:

      'p1p2...p(k-1)'='p(j-k+1)p(j-k+2)...p(j-1)'      (3)

    因此若模式串中存在满足式(3)的两个子串,则匹配仅需从模式中第k个字符与主串中第i个字符比较起继续进行

     若令next[j]=k,则next[j]表明当模式中第j个字符与主串中相应字符“失配”时,在模式中需重新和主串中该字符进行比较的字符的位置,next函数的定义为:

    next[j]={  

                      0  当j=1时;

                      Max{k|1<k<j且'p1p2...pk-1='p(j-k+1)...p(j-1)''}(此集合不为空); 

                     1 其他情况;

                 }

    所以KMP的算法可以描述为:当匹配过程中产生“失配”时,指针i不变,指针j退回到next[j]所指示的位置上重新进行比较,并且当指针j退至0时,指针i和指针j需同时加1。即若主串的第i个字符和模式串中的第1个字符不等时,应从主串的第i+1个字符起重新进行匹配。

    接下来要解决怎么求next[j]的问题。根据公式知:

         next[1]=0, 设next[j]=k,则表明

         'p1p2...pk-1'='p(j-k+1)...p(j-1)'

    此时next[j+1]可能有两种情况,

    1)若pk=pj,则表明在模式串中

            'p1..pk'='p(j-k+1)...pj'

      这就意味着next[j+1]=k+1,即next[j+1]=next[j]+1

    2) 若pk!=pj,则表明在模式串中,'p1...pk'!='p(j-k+1)..pj'

    此时可把求next函数值的问题看成是一个模式匹配的问题,整个模式串既是主串又是模式串,而当前在匹配过程中,已经有p(j-k+1)=p1,p(j-k+2)=p2,...p(j-1)=p(k-1),则当pj!=pk时,应将模式向右滑动至以模式中的第next[k]个字符和主串中的第j个字符相比较。若next[k]=k',且pj=p(k'),则说明在主串中第j+1个字符之前存在一个长度为k'(即next[k])的最长子串,和模式串中从首字符起长度为k'的子串相等。即

       'p1p2...pk''='p(j-k'+1)...pj'(1<k'<k<j)       (4)

    这就是说,next[j+1]=k'+1 即 next[j+1]=next[k]+1

    同理,若pj!=p(k'),则将模式继续向右滑动直至将模式中第next[k']个字符和pj对齐。。。依次类推,直至pj和模式中某个字符匹配成功或则不存在任何k'满足等式(4),则next[j+1]=1

    例如,已求得前6个字符的next函数值,现求next[7],因为next[6]=3,又p6!=p3,则需比较p6和p1(next[1]=1),这相当于将子串模式向右滑动,而next[1]=0,所以next[7]=1,因为p7=p1,所以next[8]=2.

    j          1 2 3 4 5 6   7 8

    模式      a b a a b c   a c

    next[j]  0 1 1 2 2 3  1 2

    上述定义的next函数尚有缺陷,例如模式'a a a a b'在和主串'a a a b a a a a b'匹配时。直接看图表示

        j           1 2 3 4 5

      模式        a a a a b

     next[j]     0 1 2 3 4

    nextval[j]  0 0 0 0 4

    代码实现:

      1 #include<stdio.h>
      2 #define MAXSTRLEN 255
      3 typedef unsigned char SString[MAXSTRLEN+1];//0单元存放串的长度
      4 int Index(SString S,SString T,int pos)
      5 {
      6     //返回子串T在主串S中第pos个字符之后的位置,若不存在,则返回0
      7     int i=pos;
      8     int j=1;
      9     while(i<=S[0]&&j<=T[0])
     10     {
     11         if(S[i]==T[j])
     12         {
     13             i++;
     14             j++;
     15         }
     16         else
     17         {
     18             i=i-j+2;
     19             j=1;
     20         }
     21     }
     22     if(j>T[0])
     23         return i-T[0];
     24     else
     25         return 0;
     26 }
     27 int Index_KMP(SString s,SString t,int pos,int next[])
     28 {
     29     //利用模式串t的next函数求t在主串s中第pos个字符之后的位置
     30     int i=pos;
     31     int j=1;
     32     while(i<=s[0]&&j<=t[0])
     33     {
     34         if(j==0||s[i]==t[j])
     35         {
     36             i++;
     37             j++;
     38         }
     39         else
     40             j=next[j];
     41     }
     42     if(j>t[0])
     43         return i-t[0];
     44     else
     45         return 0;
     46 }
     47 void get_next(SString t,int next[])
     48 {//求模式串t的next函数值并存入数组next
     49     int i=1,j=0;
     50     next[1]=0;
     51     while(i<t[0])
     52     {
     53         if(j==0||t[i]==t[j])
     54         {
     55             i++;
     56             j++;
     57             next[i]=j;
     58         }
     59         else
     60             j=next[j];
     61     }
     62 }
     63 void get_nextval(SString t,int nextval[])
     64 {
     65     int i=1,j=0;
     66     nextval[1]=0;
     67     while(i<t[0])
     68     {
     69         if(j==0||t[i]==t[j])
     70         {
     71             i++;
     72             j++;
     73             if(t[i]!=t[j])
     74                 nextval[i]=j;
     75             else
     76                 nextval[i]=nextval[j];
     77         }
     78         else
     79             j=nextval[j];
     80     }
     81 }
     82 int main()
     83 {
     84   SString s,t;
     85   int s_len,t_len,i,result;
     86   printf("请输入主串的长度:\n");
     87   scanf("%d",&s_len);
     88   s[0]=s_len;
     89   printf("输入主串的元素:\n");
     90   for( i=1;i<=s_len;i++)
     91       scanf("%d",&s[i]);
     92 
     93   printf("请输入子串的长度:\n");
     94   scanf("%d",&t_len);
     95   t[0]=t_len;
     96   int next[4];//这个数组该怎样表示会更好??
     97   int nextval[4];
     98   printf("输入子串的元素:\n");
     99   for( i=1;i<=t_len;i++)
    100       scanf("%d",&t[i]);
    101   
    102 
    103   printf("普通的模式匹配结果:\n");//再加一个计算时间的就好了,虽然知道这个花的时间较长
    104   result=Index(s,t,1);
    105   printf("得到子串在主串中的位置为:%d\n",result);
    106 
    107   printf("KMP算法模式匹配(next)的结果:\n");
    108   get_next(t,next);
    109   result=Index_KMP(s,t,1,next);
    110   printf("得到子串在主串中的位置为:%d\n",result);
    111 
    112   printf("KMP算法模式匹配(nextval)的结果:\n");
    113   get_next(t,nextval);
    114   result=Index_KMP(s,t,1,nextval);
    115   printf("得到子串在主串中的位置为:%d\n",result);
    116 }
    View Code

    运行结果:

       关于代码中对数组长度的定义的问题,用指针动态分配内存。

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 int main()
     4 {
     5     int n;
     6     scanf("%d",&n);
     7     int *p;
     8     p=(int *)malloc(n*sizeof(int));//编译正确
     9     int a[n];//编译错误。
    10 }

                 

                            

  • 相关阅读:
    vi/vim 文字处理器常用命令
    图片在容器里水平垂直居中
    谁的属性值优先被访问
    创建对象和实例
    碎碎念css
    未整理js
    表格<table>
    盒子模型
    Css文件目录结构
    链接文字<a>保持原有的字体颜色
  • 原文地址:https://www.cnblogs.com/wj204/p/3118747.html
Copyright © 2020-2023  润新知