• [转载]KMP和拓展KMP


    原文转自:http://jijiwaiwai163.blog.163.com/blog/static/1862962112012623105531177/

    1、KMP算法

        KMP算法是一种改进后的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。通过一个辅助函数实现跳过扫描不必要的目标串字符,以达到优化效果。
        KMP(O(n+m))算法与传统的BF算法(O(n*m))想比自然快了许多。
        KMP(Knuth-Morris-Pratt)算法核心思想是:在发生失配时,主串不需要回溯,而是利用已经得到的“部分匹配”结果将模式串右移尽可能远的距离,继续进行比较。这里要强调的是,模式串不一定向右移动一个字符的位置,右移也不一定必须从模式串起点处重新试匹配,即模式串一次可以右移多个字符的位置,右移后可以从模式串起点后的某处开始试匹配。
     
       对于next[]数组有位移d=len-next[len]可以看作是构成字符串s的字串(如果n%d==0,存在这样的构成),相应的重复次数也就是n/d。(其中len 为已经匹配字符串的长度)
      两个与kmp相关的简单题目:POJ 2406  ,POJ 1961
      求next[]算法模板:(未优化)
     1 void getNext(char s[],int next[])
     2 {
     3     int length=strlen(s);
     4     int i=0,j=-1;
     5     next[0]=-1;
     6     while(i<length)
     7     {
     8         if(j==-1||s[i]==s[j])
     9         {
    10             ++i;
    11             ++j;
    12             next[i]=j;
    13         }
    14         else
    15             j=next[j];
    16     }
    17 }

    求next[]算法模板:(已优化)

     1 void getNextval(char s[],int nextval[])
     2 {
     3     int length=strlen(s);
     4     int i=0,j=-1;
     5     nextval[0]=-1;
     6     while(i<length)
     7     {
     8         if(j==-1||s[i]==s[j])
     9         {
    10             ++i;
    11             ++j;
    12             //next[i]=j;
    13             if (s[i]!=s[j])
    14                 nextval[i]=j;
    15             else
    16                 nextval[i]=nextval[j];
    17         }
    18         else
    19             j=nextval[j];
    20     }
    21 }

    KMP匹配

     1 int KMP( char *t, char *s )   //s为主串,t为模式串
     2 {
     3     int lenth = strlen(t);
     4     int len = strlen(s);
     5     GetNextVal( t, lenth );
     6     int i = 0, j = 0;
     7     while ( j < len )
     8     {
     9         if ( i == -1 || s[j] == t[i] )
    10         {
    11             ++i, ++j;
    12             if ( i == lenth ) return j;
    13         }
    14         else i = nextval[i];
    15     }
    16     return -1;
    17 }
    2、扩展KMP算法
    扩展kmp既是求模式串和主串的每一个后缀的最长公共前缀
    即令s[i]表示主串中以第i个位置为起始的后缀,则B[i]表示s[i]和模式串的最长公共前缀
    显然KMP是求s[i]=模式串长度的情况,所以,扩展KMP是对KMP的拓展
    像求KMP的next数组一样,我们先求A[i],表示模式串的后缀和模式串的最长公共前缀
    然后再利用A[i]求出B[i]
    说明一下A的求法,B同理
    现在我们要求A[i],且A[1]---A[i-1]已经求出,设k,且1<=k<=i-1,并满足k+A[k]最大
    所以T[k]--T[k+A[k]-1]=T[0]--T[A[k]-1],推出T[i]--T[k+A[k]-1]=T[i-k]--T[A[k]-1]
    令L=A[i-k],若L+i-1<k+A[k]-1,由A是最长公共前缀知A[i]=L,否则,向后匹配,知道字符串失配
    并相应更新k
    时间复杂度为线性O(m+n)
     
    模板:
     1 int next[maxn],extend[maxn]; //extend[i]表示原 串以第i开始与模式串的前缀的最长匹配
     2 void EKMP(char s[],char t[])//s[]为主串,t[]为模版串
     3 {
     4     int i,j,p,l;
     5     int len=strlen(t);
     6     int len1=strlen(s);
     7     memset(next,0,sizeof(next));
     8     memset(extend,0,sizeof(extend));
     9     next[0]=len;
    10     j=0;
    11     while(1+j<len&&t[j]==t[1+j])j++;
    12     next[1]=j;
    13     int a=1;
    14     for(i=2; i<len; i++)
    15     {
    16         p=next[a]+a-1;
    17         l=next[i-a];
    18         if(i+l<p+1)next[i]=l;
    19         else
    20         {
    21             j=max(0,p-i+1);
    22             while(i+j<len&&t[i+j]==t[0+j])j++;
    23             next[i]=j;
    24             a=i;
    25         }
    26     }
    27     j=0;
    28     while(j<len1&&j<len&&s[j]==t[j])j++;
    29     extend[0]=j;
    30     a=0;
    31     for(i=1; i<len1; i++)
    32     {
    33         p=extend[a]+a-1;
    34         l=next[i-a];
    35         if(l+i<p+1)extend[i]=next[i-a];
    36         else
    37         {
    38             j=max(0,p-i+1);
    39             while(i+j<len1&&j<len&&s[i+j]==t[j])j++;
    40             extend[i]=j;
    41             a=i;
    42         }
    43     }
    44 }

     str编号是从0~len-1,而next值编号是从1~len

  • 相关阅读:
    深入正则表达式(0):正则表达式概述
    讲透学烂二叉树(二):图中树的定义&各类型树的特征分析
    讲透学烂二叉树(一):图的概念和定义—各种属性特征浅析
    Gzip之后继者Brotli浅析之CDN厂商的智能压缩,服务器Brotli设置
    ECMAScript进化史(1):​话说Web脚本语言王者JavaScript的加冕历史
    nginx网站限速限流配置——网站被频繁攻击,nginx上的设置limit_req和limit_conn
    linux添加用户,修改用户密码,修改用户权限,设置root用户操作
    nginx 限制ip访问,禁止非法域名指向本机ip——防止被别人绑定域名到自己IP的方法
    centos8 新增ssh自定义端口与屏蔽默认22端口。
    1g云主机升级centos8不满足centos 8 至少2g内存要求,linux虚拟内存来凑
  • 原文地址:https://www.cnblogs.com/GBRgbr/p/3068099.html
Copyright © 2020-2023  润新知