KMP算法

来源:互联网 发布:淘宝平拍裤子褶皱手法 编辑:程序博客网 时间:2024/06/15 23:22

KMP算法

silver叨逼叨

这个算法比较难以理解实际使用也不常见但不可否认其优秀性,希望读者自行取舍.
但是该算法算是以空间换时间 在算法进行预处理用以缩短程序所需时间,按照学习顺序这应该是第一次接触相信多理解这类会对后边学习有所帮助
该算法的核心就是求得Next数组
时间复杂度为O(m+n)

算法简介

KMP算法对于任何模式和目标序列,都可以在线性时间内完成匹配查找,而不会发生退化,是一个非常优秀的模式匹配算法。

给定两个字符串O和f,长度分别为n和m,判断f是否在O中出现,如果出现则返回出现的位置。常规方法是遍历a的每一个位置,然后从该位置开始和b进行匹配,但是这种方法的复杂度是O(nm)。kmp算法通过一个O(m)的预处理,使匹配的复杂度降为O(n+m)。

算法思想

我们首先用一个图来描述kmp算法的思想。
在字符串O中寻找f,当匹配到位置i时两个字符串不相等,这时我们需要将字符串f向前移动。常规方法是每次向前移动一位,但是它没有考虑前i-1位已经比较过这个事实,所以效率不高。
事实上,如果我们==提前计算某些信息(next数组)==,就有可能一次前移多位。假设我们根据已经获得的信息知道可以前移k位,我们分析移位前后的f有什么特点。

我们可以得到如下的结论:
A段字符串是f的一个前缀。
B段字符串是f的一个后缀。
A段字符串和B段字符串相等。
所以前移k位之后,可以继续比较位置i的前提是f的前i-1个位置满足:长度为i-k-1的前缀A和后缀B相同。只有这样,我们才可以前移k位后从新的位置继续比较。

所以kmp算法的核心即是计算字符串f每一个位置之前的字符串的前缀和后缀公共部分的最大长度(不包括字符串本身,否则最大长度始终是字符串本身),即next数组。获得f每一个位置的最大公共长度之后,就可以利用该最大公共长度快速和字符串O比较。当每次比较到两个字符串的字符不同时,我们就可以根据最大公共长度将字符串f向前移动(已匹配长度-最大公共长度)位,接着继续比较下一个位置。事实上,字符串f的前移只是概念上的前移,只要我们在比较的时候从最大公共长度之后比较f和O即可达到字符串f前移的目的。

文字看多了眼睛花 前方高能请谨慎阅读!!!

  1. 为了更直观起见,我将模式串进行放大并影分身!==你看到的是两个模式串,但其实是一个==(后面不再提醒)。
    s

  2. 那,这样我们得到一段字符串aba,它只存在一个后缀a,与其本身的前缀a匹配。如下图。

  1. 则,这个位置的next值就为1。即next[3]=1;如下图.

  2. 同理,如果我们在5位置切割,如下图,得到next[5] = 3;如下图。

  3. 依次类推,所有的位置next值都可以得到,如下图。

那么next值是什么,就是在当前位置匹配失败的时候,下标应该改变到的位置,继续进行匹配。

既然next数组这么重要,我们必然要整理出一套合理实用的算法框架

void GetNext(int next[],char p[]){    //p[k]表示上图中下面的那一串 p[j]表示上图中下面的那一串    int length = strlen(p);    next[0]=0;    int k=0;    int j=1;    while(j<length){        if(p[k]==p[j]||k==0){            next[j]=k;            k++;            j++;        }        else{            k=next[k];        }    }}

一直在强调next数组的重要性以及如何实现但是却未曾提及next数组要如何使用它、又如何能让我们的匹配算法下降到线性O(n)

简单来说,要么直接找到,要么一直往next数组往前推,最多回推到0。

//k为主串 p为待匹配串 next为p的next数组int KMP(char k[],char p[],int next[]){    int i=0,j=0;    int n=strlen(k);    int m=strlen(p);    while(i<=n){        while (j!=0&&k[i]!=p[j]){            j=next[j];        }        if(j==m) return i-m;//匹配成功,返回匹配位置        else{j++; i++;}    }    return -1;//匹配失败}
原创粉丝点击