KMP算法

字符串匹配通常有两种常见的算法,BF(Brute Force)算法和KMP算法,下面分别说明一下,假定目标串S,长度为n,模式串P,长度为m
BF算法是最直观的算法,从目标串S的起点0到n-m,依次遍历
伪代码以下
for i <- 0 to n-m
    j <- 0//每次P的指针j回溯到起点,S的指针i加一
    while j < m   
        if S[i+j] = P[j]
            j++
        else
            break
    if j = m
        return i
return -1
BMP算法利用了模式串P自身的一些属性,假定S[i,  i+k-1] = P[0, k-1],可是S[i+k]!=P[k],咱们能够找到k',k'知足如下条件
1.P[0 ,k'-1] = P[k-k', k-1]
2.k'是全部知足条件1中最大的
而后把整个模式串P之间向右平移k-k'次,也就是把P[k']移动到P[k]的位置,如图所示
 
显然这么作的话,P[0, k'-1]与S[i+k-k' ,i+k-1]之间已经匹配好了,咱们只须要比较P[k']与S[k]之间的是否相等。
等等,是否是落下了什么,正确的匹配会不会发生在模式串P向右平移[1, k-k'-1]之间的某次呢?不妨假设向右平移了c次,c属于上述区间,以下图
 
如果发生了正确匹配,必然有P[0, k-c-1] = S[i+c , i+k-1] = P[c+1, k]。另外k-c-1 > k'-1,显然违反了k'的性质2,假设不成立,也就是说正确的匹配不会发生在模式串P向右平移[1, k-k'-1]之间的某次,咱们能够放心大胆的平移k-k'了。
 
在大多数经典论述中,上面所说的k'就是next[k],接下来就该考虑如何求解next[k]了,根据我看到的资料,关于next[k]的解法应该是有两种,区别在因而否考虑P[k]和P[k']是否相同,先说不考虑是否相同的状况。首先,next[0] = -1,表示从P的头部开始比较,假定已知k' = next[k],则P[0, k'-1] = P[k-k', k-1],要求next[k+1]
1.若是P[k'] = P[k],则P[0, k'] = P[k-k', k],next[k+1] = next[k] = k'
2.若是P[k'] != P[k],至关于模式串P本身与本身比对,k <- next[k],而后重复上述过程
附代码
void getNext(char *p, int *next)
{
    next[0] = -1;
    int j = 0;
    int k = -1;
    while (j < strlen(p))
    {
        if(k == -1 || p[k] == p[j])
        {
            k++;
            j++;
            next[j] = k;
            /*if(p[k] == p[j])
                next[j] = next[k];
            else
                next[j] = k;*/
        }
        else
            k = next[k];
    }

}

int KMP(char *s, char *p)
{
    int next[20];
    getNext(p, next);
    int i = 0, j = 0;
    while (i < strlen(s))
    {
        if(j==-1 || s[i]==p[j])
        {
            i++;
            j++;
        }
        else
        {
            j = next[j];
        }
        if(j == strlen(p))
            return i-j;
    }
    return -1;
}
第二种算法是考虑了P[k]和P[k']是否相同,对于按照第一种算法算出k',若是P[k]和P[k']不相等,和第一种彻底相同;不然k' <- next[k'],注意这句话中的隐含操做,把next[k']赋给k',那么next[k']中存储的又是什么呢,其实是一种递归操做,找出最大的某下标d,使得P[d]!=P[k],而P[k]=P[k']=P[next[k']]=......P[pre_d],其中next[pre_d]=d,结果就是next[k] = next[k']=...=next[pre_d]=next[d],即把next[d]的值赋给其余,再次强调此时P[d]!=P[k]。能够直观地感受出这种算法更加高效,由于若是P[k] = P[k'],则P[k'] != S[i+k],模式串仍然须要继续向右平移,而第二种算法一步到位。区别如表格所示
 
getNext的函数也改成以下形式
void getNext(char *p, int *next)
{
    next[0] = -1;
    int j = 0;
    int k = -1;
    while (j < strlen(p))
    {
        if(k == -1 || p[k] == p[j])
        {
            k++;
            j++;
            //next[j] = k;
            if(p[k] == p[j])
                next[j] = next[k];
            else
                next[j] = k;
        }
        else
            k = next[k];
    }

}

 

而匹配算法int KMP(char *s, char *p)与原来的彻底相同。
相关文章
相关标签/搜索