KMP算法的理解,伪代码,c代码实现

一、字符串问题形式化定义:假设文本是一个长度为n的T[1..n],而模式是一个长度为m的数组P[1..m],其中m<=n,若是有T[s+1..s+m]==P[1..m],那么就称模式P在T中出现。s为有效偏移,不然称为无效偏移。算法

二、方法:首先基于模式进行预处理,而后找到全部有效偏移(匹配)。数组

几种方法的预处理时间和匹配时间函数

算法优化

预处理时间spa

匹配时间code

朴素算法orm

0字符串

o((n-m+1)*m)it

有限自动机io

o(m|全部有限长度字符串的集合|)

o(n)

KMP

o(m)

o(n)

Rabin-karp

0(m)

o((n-m+1)*m)

三、朴素字符串匹配算法:经过循环的方式找到全部有效偏移s。有效偏移s的可能有n-m+1个,每次匹配须要m次,所以共需匹配(n-m+1)*m次。

伪代码:

NAIVE-STRING-MATCHER(T,P)

1. n=T.length

2. m=P.length

3. for s=0 to n-m

4.        if P[1..m] == T[s+1..s+m]

5.                 printf "Pattern occurs with shift" s

缺点:忽略了检测无效s值时得到的文本信息。

四、Rabin-Karp算法:初等数论的概念。暂且不研究。

五、利用有限自动机进行字符串匹配:首先创建好一个有限自动机,而后根据有限自动机进行匹配。

有限自动机:包括五个元素,全部状态的集合,初始状态,接收状态的集合,有限输入字母表,转移函数。

六、KMP算法:经过前缀函数避免对无用偏移进行检测。也能够避免在自动机匹配中,对整个转移函数的计算。主要缘由在于字符串中存在部分匹配的现象。

本质:针对待匹配的模式串的特色,判断它是否有重复的字符,从而找到它的前缀与后缀,进而求出相应的Next数组,最终根据Next数组而进行KMP匹配

next数组:记录下字符串P中的共有元素的位置,即第一个共有元素向后便宜多少能够到达第二个相同的元素哪儿。

"部分匹配"的实质是,有时候,字符串头部和尾部会有重复。好比,"ABCDAB"之中有两个"AB",那么它的"部分匹配值"就是2("AB"的长度)。搜索词移动的时候,第一个"AB"向后移动4位(字符串长度-部分匹配值),就能够来到第二个"AB"的位置。

大体思路:

kmp的比较函数:

1.首先初始化好NEXT数组, next[0]=0,next[1]=1

2.循环查找模式P是否在T中

    1)首先比较P[i] == T[j],若是相等,继续比较下一个,不然执行2.2)

    2)令j=next[j],继续比较(这一步避免了回溯)

    3)若是j==0; 代表没有匹配,则i++, j++

3.直到找到P在T中的位置或者T已经被比较晚结束。

当发生失配的状况下,j的新值next[j]取决于模式串中T[0 ~ j-1]中前缀和后缀相等部分的长度, 而且next[j]刚好等于这个最大长度

next数组的初始化

1.定义next数组, 令next[0]=0

2.从str[1]开始循环计算对应的next数组

3.    循环计算next[j]的值

4.        从k往前找到某个p[j]=p[k],若是相等则next[j]=k+1.(优化:若是p[k+1]和p[j+1]相等,next[j+1]=next[k+1])

5.        不然,令k=next[k]继续回溯查找,直到找到相等的为止。

6.        若是i=-1,则代表模式P中没有p[j]相同的前缀,令next[j]=1   

#include <stdio.h>
int nextArr(char* str, int* next, int m){
    assert(m>1);
    next[0] = -1;
    int j=0,k=-1;
    while(j<m)
        if(k==-1||(str[j]==str[k]){
            j++;
            k++;
            if(str[j]==str[k]){
                next[j]=next[k];
            }else{
                next[j]=k;
            }
        }else{
            k=next[k];
        }            
    }
    return 0;
}

int kmpCmp(char* t, char* p, int n, int m){
    int i=0, j=0;
    int next[10];
    next[0] = 0;
    nextArr(p, next, m);
    for(i=0; i<m; i++){
        if(t[i]==p[j]){
            if(j==m-1){
                printf("%d\n", i-m+1);
                return 1;
            }else{
                j++;
                continue;
            }
        }else{
            j=next[j];
        }
        if(j==0){
            j=0;
        }
    }
    return 0;
}
相关文章
相关标签/搜索