• 【算法】KMP


    @

    一. 暴力匹配

    字符串匹配的最直接的方法就是暴力匹配,而KMP算法也是基于暴力算法进行改进。暴力匹配的思想如下:

    1. 对于文本串T和模式串P,从模式串P的第 0 号位置、文本串第 (i_0) 号位置开始逐一比对;
    2. 比对到中间某个时刻,若(T[i ] == P[j]),则比对继续进行,(i++, j++)
    3. 如果比对失败,则从模式串第0位和文本串的第(i_0 + 1)位继续进行
      在这里插入图片描述

    但是(T[i_0 , i))(P[0, j))比对成功意味着(T[i_0 , i))(P[0, j))完全相同的,掌握了(P[0, j))那么也就意味着掌握了(T[i_0 , i)),下一轮的比对方案完全可以提前预知。

    例1
    在这里插入图片描述
    在图中的比对过程中,主串中的 x 和模式串中的 y 失配,根据模式串中 y 以前的内容可以获知主串对应部分的内容。如果在下一步的比对过程中直接将主串中的 x 和模式串中的 e 进行比对,可以省去 6 次比对

    二.KMP的基本思想

    在对暴力破解的算法的分析中发现,对比在某个位置失败意味着在这之前的比对完全成功,主串中失配字符前的一段内容已经完全获知。利用这一点,对暴力算法可以进行两个方面的优化:

    1. 避免主串的回溯。暴力匹配当比对失败后,文本串的第(i_0 + 1)位、(i_0 + 2)位、……、(i-1)位的对比结果完全可以推导出来,没有必要再进行比对尝试;
    2. 模式串快速移动。基于和上面相同的原因,模式串的新的比对位置不需要从 0 开始。如例一中的模式串,字符y和字符e的前面都包含了"abc"这一部分,因此y前面的部分能和主串匹配成功,那么e前面的也一定能匹配成功。

    因此对于模式串中的每个位置 j ,都能提前找到一个替代位置。

    例2
    在这里插入图片描述

    模式串"abababca",对字符c而言,2号位的 a 和4号位的 a 都是能够在字符 c 发生失配时的一个可选择的位置

    在诸多可选的继任位置中,位置下标越大,意味着已经成功匹配的长度越长,剩下需要比对的位置也就越少,因此 j 的继任位置(next[j])定义为:

    [next[j] = max(k | p_0 p_ 1...p_{k - 1} = p_{j - k}p_{j - k + 1}...p_{j - 1}) ]

    通常定义(next[0] = -1)或者(next[1] = 0)(当字符串的下标从1开始时),这种规定是假想在模式串的起始位置的前一个有一个通配哨兵。

    三.next[]的求法

    1. 暴力求解

    根据(next[j])的定义,从逐一枚举字符P[j]的真前缀和真后缀,找出相等的真前缀和真后缀的长度,取长度的最大值即为(next[j])

    2. 递推求解

    假设已经求得(next[0, ... , j]),递推求解(next[j + 1])

    (next[j])已知意味着(P[0, 1, ..., next[j] - 1])(P[j - next[j], ... , j - 1])是相等的,并且这个相等的部分是最大的,求取(next[j + 1])时,只需要考察(P[next[j]] == P[j])是否成立。如果成立,(next[j + 1] = next[j] +1) ,如果不成立,再考察(P[next[next[j]]] == P[j])是否成立,依次类推,最终会收敛于(next[0] + 1 = 0)
    在这里插入图片描述
    插图来自视频

    void buildNext(string str, int nt[]){
        int len = str.size();
        nt[0] = -1;
        int t = nt[0], j = 0;
        while(j < len - 1){
            if(t < 0 || str[j] == str[t]){
                nt[++j] = ++t;
            }else{
                t = nt[t];
            }
        }
    }
    

    四.KMP算法

    在求解了next数组之后,kmp算法变得非常简单了。

    int kmp(string str1, string str2){
        int nt[str2.size()];
        buildNext(str2, nt);//构建next表
        int i = 0, j = 0;
        while(i < str1.size() && j < str2.size()){//逐步比对
            if(j < 0 || str1[i] == str2[j]){//比对成功时,前进一位,j < 0表示和通配符比对成功
                i++; j++;
            }else{//对比失败,找到新的位置比对
                j = nt[j];
            }
        }
        return i - j;
    }
    
  • 相关阅读:
    关于GIS从业人员的定位
    《企业应用架构模式》读书笔记(4)
    各大网络、软件巨头涉足Web GIS
    流水帐(2005.5)
    Xtreme Suite 和 Toolkit 9.6发布
    一个算法问题
    推荐2个最近使用的软件
    linux之pmap命令
    linux 文件系统简介
    百度脚本笔试题两道
  • 原文地址:https://www.cnblogs.com/vinnson/p/13451411.html
Copyright © 2020-2023  润新知