码迷,mamicode.com
首页 > 编程语言 > 详细

KMP算法

时间:2019-08-22 20:36:37      阅读:106      评论:0      收藏:0      [点我收藏+]

标签:mp算法   sharp   重复   复杂   过程   clu   max   amp   turn   

KMP是在解决查询某一字符串是否在另一个字符串内的匹配问题时,能高效减少重复暴力的匹配过程从而缩短了查询时间,优化了算法的时间复杂度。

举个例子,在abaababc中查询有无abab:

0 1 2 3 4 5 6 7        index[]    
a b a a b a b c         s[]            //在s[3]!=t[3]处匹配失败,找t中最近的一个与i-1相同的字符
a b a b                 t[]
0 1 2 3 4 5 6 7        index[]    
a b a a b a b c         s[]             //由于t[0]和s[1]肯定不等,这里就是可优化的地方,找到最近的a是t[0],所以将t[0]与t[2]对齐
    a b a b             t[]
0 1 2 3 4 5 6 7        index[]    
a b a a b a b c         s[]
      a b a b           t[]            //后面s[3]!=t[2],后拉一步匹配成功

上述只是简单的道了一个优化处,我们来对比暴力算法从而放大该优点,可以更好理解真正的kmp。暴力算法就是t中从头开始与s[i]一个个匹配,比如i=1匹配到i=6若匹配失败,则下一层循环就是从i=2开始匹配,再重复,i=3.。。。。。当i=1匹配到i=6时,不让i=2,也就是让i不往后退,而是让j,即t【】回溯,这就是kmp的目的。相当于在上述三步中,在第二步之前还有一步多余的t[0]与s[1]匹配,其实是毫无意义多余的,如果是aaaaaaaaaaaaabab的字符串,前面有很多重复,但暴力求解就会浪费很多无意义的匹配,kmp就是可以跳过大部分重复,或说利用一些规则,就是前后公共缀,

如a b a b a b 字符串,

前缀有a,a b,a b a, a b a b, a b a b a , a b a b a b;

后缀有b,  a b , b a b , a b a b ,b a b a b , a b a b a b;

前后缀一样的就有a b, a b a b ,a b a b a b,那么这在匹配中有什么用呢?

0 1 2 3 4 5 6 7 8 9 10 11 12
a c c a b c d a b c d a b d i a b c d a b d         j    //可以看到,在s[9]处匹配失败,那么看前面匹配成功的字符串中,由于前后缀只有ab匹配,相当于中间那部分在t暴力往后移与s匹配过程都不可能相等,只有34处与78处是可以匹配的,所以一步到位,t[0]直接指向s[7] a b c d a b d

 

kmp关键就在这,i是一直向后走不回溯,这样才能减少遍历次数,而让t[]匹配,j可回溯回0,即t[0],然后和s[i]一个个匹配,

两点: 1.s【】中的i不回溯,t中的j可回溯,t滑块进行灵活匹配

    2.前后缀公共区域,每次匹配失败是,将t[0]指向s[i],若有n长的公共前后缀,就将t向左移n位,即t[0]指向s[i-n]

#include<stdio.h>
#include<string.h>
#define max 1000000
char s[max],t[max];
int n,m,next[max];

int kmp()
{
    int i=0,j=0,r=0;
    while(i<n){
        if(s[i]==t[j])
        {
            i++;
            j++;            //进行下一个字符的匹配 
        }else{
            j=next[j];        //否则回溯j,最开始将t[0]与s[i]对齐,由于t中0~j之前有n个公共前后缀重复, 
        }                    //即t[0:n]==s[i-n-1:i-1],n就是我们要算的长度getnext(),可以发现左移t[0]的长度等于公共前后缀  
        if(j==m&&s[i]==t[j])    //如果目标字符串最后一个字符相匹配则成功 
        {r=1;break;}   
    }
    if(r=1)
    return i-m;                        //成功则返回所在的字符首部索引 
    else 
    return 0;
    
}

void getnext()
{
    int i=0,j,r,p;
    for(i=0;i<m-1;i++){
        j=0;r=0;
        while(t[j]==t[m-i-1+j])
        {
            next[i]++;
            j++;
            if(j==(i+1)&&t[j]==t[m-i-1+j])
            {r=1;break;}
            else r=0;
        }
        if(r=0) next[i]=0;
    }
                                    //上面得到的是正常索引的next公共前后缀 
    for(i=m-1;i>0;i--)                //这步是将next向后推一位,因为kmp匹配时,当s[i]!=t[j]时,说明第j位匹配失败,
        next[i]=next[i-1];            //需要看前0~j-1位与i之前的公共区域,所以要后推索引更方便  
    
}

int main()
{
    scanf("%s",&s);
    scanf("%s",&t);
    n=strlen(s);
    m=strlen(t);
    getnext();
    for(int i=0;i<m;i++){
        printf("%d",next[i]);
    }
    printf("\nkmp=%d",kmp());
} 

 

KMP算法

标签:mp算法   sharp   重复   复杂   过程   clu   max   amp   turn   

原文地址:https://www.cnblogs.com/Theo-sblogs/p/11396615.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!