字符串查找算法kmp

给定一个文本串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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值