zoukankan      html  css  js  c++  java
  • 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)与原来的完全相同。
  • 相关阅读:
    Android课程---Activity的跳转与传值(转自网上)
    Android课程---Activity中保存和恢复用户状态
    Android课程---Activity 的生命周期
    Android课程---Activity的创建
    初学JAVA随记——练习写代码(8种数据类型)
    资料——UTF-8
    资料——ASCII码
    初学JAVA随记——8bit(1byte)的取值范围是+127到—128
    初学JAVA随记——变量与常量
    进制转换
  • 原文地址:https://www.cnblogs.com/rain-lei/p/3632202.html
Copyright © 2011-2022 走看看