• KMP算法复习


    KMP算法

    在字符串匹配中,我们一般是BF算法,暴力破解。当然上完数据结构课之后,我们知道可以通过KMP进行优化。

    上数据结构这门课的时候,我没有好好听KMP,本篇文章参考程杰老师的《大话数据结构》中KMP算法一章节记录的笔记,可以的话请阅读原书中该章节,这里相当于原书的复述。

    这边引用书中的一句话:任何有难度的知识和技巧,都不是那么容易掌握的。我尽管已经朝着通俗易懂的方向努力,可有些数据结构,特别是经典算法,是几代科学家的智慧结晶,因此要掌握他们还是需要读者的全力投入。

    1.Next串

    关于Next串,根据Pattern串进行得出的回溯串。

    案例1

    1)Next数组,第一位Next[1]=0

    2)j=2的时候,j从0到j-1就只有字符“a“,那么给定一个1

    3)j=3的时候,j从0到j-1就只有字符“ab",那么给定一个1

    往后同理

    案例2

    1)Next数组,第一位Next[1]=0

    2)j=2的时候,同上,Next[2]=1

    3)j=3的时候,同上,Next[3]=1

    4)j=4的时候,同上,Next[4]=1

    5)j=5的时候,此时j由1到j-1的串是“abca”,前缀“a”,后缀“a”,推算k的值为2,则Next[5]=2

    6)j=6的时候,串为“abcab”,前缀“a”,后缀“a”,我们可以回溯到3,Next[6]=3

    我们可以根据经验得到如果前后缀一个字符相等,k的值是2,两个字符k的值是3,第n个就是n+1

    案例3

    1)Next数组,第一位Next[1]=0

    2)j=2的时候,同上,Next[2]=1

    3)j=3的时候,同上,Next[3]=1

    4)j=4的时候,串为“aba”,前缀“a”,后缀“a”,则Next[4]=2

    5)j=5的时候,串为“abab”,前缀“ab”,后缀“ab”,则Next[5]=3

    6)j=6的时候,串为“ababa”,前缀“aba”,后缀“aba”,则Next[6]=4

    7)j=7的时候,串为“ababaa”,只有“a”符合,则Next[7]=2

    8)j=8的时候,串为“ababaaa”,只有“a”符合,则Next[8]=2

    9)j=9的时候,串为“ababaaab”,只有”ab“符合,则Next[9]=3

    案例4

    1)Next数组,第一位Next[1]=0

    2)j=2的时候,同上,Next[2]=1

    3)j=3的时候,前缀为“aa”与后缀“aa”相同,Next[3]=2

    4)j=4的时候,前缀为“aaa”与后缀“aaa”相同,Next[4]=3

    .....

    2.朴素KMP

    通过这么多,我们可以来看一下代码,下面是朴素next数组和KMP,我们这边next是朴素KMP的求解方案

    void get_next(string T, int *next) { // T是匹配串
        int i = 1, j = 0;
        next[1] = 0;
        while(i < T[0]){// T[0]存储T的长度
            if(j == 0 || T[i] == T[j]) {
                i++;
                j++;
                next[i] = j;
            }else {
                j = next[j]; // 回溯
            }
        }
    }
    int KMP(string S, string T, int pos){
        int i = pos, j = 1;
        int next[255];
        get_next(T, next);
        while(i <= S[0] && j <= T[0]){
            if(j == 0 || S[i] == T[j]){
                i++;
                j++;
            }else j = next[j];
        }
        if(j > T[0]) return i - T[0];
        else return 0;
    }
    

    3.KMP算法改进

    朴素KMP存在一些缺陷。例如:S="aaaabcde",子串 T = "aaaaax"分别是012345,在开始,我们发现b与a不相等,因此j=next[5]=4,紧接着回溯3,直到0。所以我们在算法改进中进行优化操作,若与之前相等,则进行与之前相等操作。

    void get_next(string T, int *next) { // T是匹配串
        int i = 1, j = 0;
        next[1] = 0;
        while(i < T[0]){// T[0]存储T的长度
            if(j == 0 || T[i] == T[j]) {
                i++;
                j++;
                if(T[i] != T[j]) // 如果与前缀字符串不一样
                    next[i] = j; // 进行设置j为i
                else
                    next[i] = next[j]; // 否则进行赋值操作
            }else {
                j = next[j]; // 回溯
            }
        }
    }
    

    4.nextval数组改进推导

    案例

    1)开始nextval[1] = 0

    2)第二位字符b的nextval为1,二第一位就是“a”,不相等,维持原值

    3)第三位字符a的nextval为1,“a”与“a”相等,则变换为0

    4)第四位字符b的nextval为2,“b”与“b”相等,则变换为1

    5.总结

    其实只要细心耐心,KMP算法理解后也不是想象中那么麻烦,写起来还蛮方便的。但是在算法调试中,KMP确实不太好写,我们记忆住原理优先。

  • 相关阅读:
    BZOJ 3506 机械排序臂 splay
    BZOJ 2843 LCT
    BZOJ 3669 魔法森林
    BZOJ 2049 LCT
    BZOJ 3223 文艺平衡树 splay
    BZOJ 1433 假期的宿舍 二分图匹配
    BZOJ 1051 受欢迎的牛 强连通块
    BZOJ 1503 郁闷的出纳员 treap
    BZOJ 1096 ZJOI2007 仓库设计 斜率优化dp
    BZOJ 1396: 识别子串( 后缀数组 + 线段树 )
  • 原文地址:https://www.cnblogs.com/littlepage/p/12238844.html
Copyright © 2020-2023  润新知