KMP模式匹配算法模板

来源:互联网 发布:边听歌边学英语的软件 编辑:程序博客网 时间:2024/05/29 07:04

next数组


kmp算法的关键在于next数组的求解,next的值仅取决于子串本身而和主串无关。我们可以从定义分析出发用一种递推思想求得next的函数值。这里以数组下标从0开始为例


这里又有一种将子串看成"主串"的思想,利用前面的next信息,递推构造后面的值

关键代码为:

while(j<len2-1){        if(k==-1||t[j]==t[k])//如果k为-1,即到了递推前部边界,由下面的语句可得next[++j]=0           next[++j]=++k;   //如果t[j]==t[k],则相当于递推地直接获得了j+1的"最大满足"k         else k=next[k];//递推,降低k看前后是否匹配    }


代码示例

#include<bits/stdc++.h>#define max_size 200using namespace std;char p[max_size];char t[max_size];int next[max_size+5];int len1,len2,pos;void get_next(char *t,int *next){    int j=0;    int k=-1;    next[j]=k;    while(j<len2-1){        if(k==-1||t[j]==t[k]) next[++j]=++k;        else k=next[k];    }}int index_kmp(char *p,char *t,int *next){    int i=pos-1;    int j=-1;    get_next(t,next);    while(i<len1&&j<len2)    {        if(j==-1||p[i]==t[j]){            ++i;            ++j;        }        else j=next[j];    }    if(j>=len2) return i-len2+1;    else return 0;}int main(){    cin>>p>>t>>pos;    len1=strlen(p);    len2=strlen(t);    int ans=index_kmp(p,t,next);    cout<<ans<<endl;    return 0;}

对于get_next函数来说,若t的长度是m,因只涉及到简单的单循环,其时间复杂度为O(m)。而由于i的值不回溯,使得index_kmp()算法效率得到了提高,其时间复杂度为O(n+m)。相较于朴素模式匹配算法的O((n-m+1)*m)来说,是要好一些。

需要强调的是,kmp算法仅当子串与主串之间存在许多“部分匹配”的情况下才能体现出它的优势,否则两者差异并不明显。


get_next函数的改进

有一类情况:例如主串为aaaabcde  子串为aaaaax ,在匹配的过程中会发现有些步骤是多余的。

因为对于主串和子串大面积相同的情况,next数组的移动并不明显,于是我们将代码改进

void get_next(char *t,int *next){    int j=0;    int k=-1;    next[j]=k;    while(j<len2-1){        if(k==-1||t[j]==t[k]){        ++j;        ++k;        if(t[j]!=t[k])//若当前字符与前缀字符不同,则当前的k为next在j位置的值 (这部分同上)         next[j]=k;        else //如果与前缀字符相同,则将前缀字符的next值直接赋给next在j位置的值(改进部分)         next[j]=next[k];        }        else k=next[k];//如果与前缀字符相同,     }}


e.g

 


总结

改进过的kmp算法,它是在计算出next值的同时,如果a位字符与它next值指向的b位字符相等,则该a位字符的next就指向b位的next值;如果不等,则该a位的next(改进)的值就是它自己a位的next(原)的值。



1 0
原创粉丝点击