字符串查找算法kmp

来源:互联网 发布:钉钉办公软件有mac版 编辑:程序博客网 时间:2024/06/04 18:24

给定一个文本串s,和一个匹配串p,要求查找p第一次在s中出现的位置。

常见的方法如暴力搜素,逐个匹配s[i],p[j],若匹配,下标后移。不匹配,i回溯到这次匹配前的下一位置,j置为0,重新匹配。最坏情况,时间复杂度O(n*m)。

int violenceSearch(char *s,char *p){        int i=0,j=0;    int lenp=strlen(p);    int lenS=strlen(s);    while(j<lenp&&i<lens){        if(s[i]==p[j]){            i++;            j++;        }        else{            i=i-j+1;            j=0;        }    }    if(j==len) return i-j;    return ;}

每次s[i] , p[j]不匹配总要将i回溯到到下一位置,j置0,这样做可能还是会导致不匹配,又要i回溯,j置0……造成不必要的开销,kmp查找算法的做法就是令i不回溯,当不匹配的时候令j置为next[j], 再将s[i]与p[next[j]]进行匹配,这种做法的好处是不用回溯i,最坏情况时间复杂度O(n+m)。

int kmpSearch(char *s,char *p){     int i=0,j=0;    int lens=strlen(s);    int lenp=strlen(p);    while(i<lens&&j<lenp){        if(j==-1||s[i]==p[j]){//当j==-1时,表示将匹配串的首位和文本串进行匹配,即i=i+1,j=0.            i++;            j++;        }        else            j=next[j];    }    if(j==lenp)return i-j;    return ;}

这里维护的next[j]是在匹配串p上p[j]之前(不包含p[j])的子串的相同最长前缀后缀的长度

如给定匹配串ABCDABD,对应next[]:

next[0]=-1

kmp的精髓应该就在求解next[]数组上,即知道next[0….j],如何求解next[j+1]:

•若p[k] == p[j],则next[j + 1 ] = next [j] + 1 = k + 1;
•若p[k ] ≠ p[j],如果此时p[ next[k] ] == p[j ],则next[ j + 1 ] = next[k] + 1,否则继续递归前缀索引k = next[k],而后重复此过程。 相当于在字符p[j+1]之前不存在长度为k+1的前缀”p0 p1, …, pk-1 pk”跟后缀“pj-k pj-k+1, …, pj-1 pj”相等,那么是否可能存在另一个值t+1 < k+1,使得长度更小的前缀 “p0 p1, …, pt-1 pt” 等于长度更小的后缀 “pj-t pj-t+1, …, pj-1 pj” 呢?如果存在,那么这个t+1 便是next[ j+1]的值,此相当于利用已经求得的next 数组(next [0, …, k, …, j])进行P串前缀跟P串后缀的匹配。

void getnext(char *p,int next[]){    int k=-1;    int j=0;    int next[0]=-1;    int len=strlen(p);    while(j<len-1){        if(k=-1||p[k]==p[j]){//p[k]表示前缀,p[j]表示后缀。                            //k=-1时,这时之前的串没有相等的前缀后缀,即next[j+1]=0;            ++j;            ++k;                            next[j]=k;              }        else            k=next[k];     }}

其实next [j]数组只要将各个最大前缀后缀(这时包括next[j]这个元素)的公共元素的长度值右移一位,且把初值赋为-1 即可。

next数组的优化:

当匹配串p[j]!=s[i]的时候,这时就要用p[next[j]]与s[i]进行匹配但是如
果p[j]==p[next[j]],就一定会导致这次的匹配失败。所以不允许p[j]==p
[next[j]],如果相等,就再次递归让p[next[next[j]]]与s[i]进行匹配
(即next[j]=next[next[j]])。

void GetNextval(char *p,int next[]){    int k=-1;    int j=0;    int next[0]=-1;    int len=strlen(p);    while(j<len-1){        if(k=-1||p[k]==p[j]){  //p[k]表示前缀,p[j]表示后缀。            //k=-1时,这时之前的串没有相等的前缀后缀,即next[j+1]=0;            ++j;            ++k;            if(p[j]!=p[k])                next[j]=k;//没改动前只有这行赋值。            else                next[j]=next[k];//因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k]                }        else            k=next[k];     }}

唯一参考:http://blog.csdn.net/v_july_v/article/details/7041827

精简了参考的这篇经典博文,是学习的一个记录,很多细节都没详细的阐述。

0 0
原创粉丝点击