KMP算法详解

Posted zjf-293916

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了KMP算法详解相关的知识,希望对你有一定的参考价值。

KMP算法主要用于解决包含问题,即存在两个字符串str1和str2,判断str1字符串中是否包含字符串str2,包含则返回str2对应在str1中的字符串的首字符的位置,否则返回-1;

例如:str1="abc123def"  str2="123d"   ,str1的长度为N,      str2的长度为M,则返回3;

      那么如何解决这类问题呢?正常的解法所用时间复杂度为O(N*M),即一个一个字符进行匹配,匹配不成功,则str1索引向后移动一位,str2重新从首字符进行匹配。

     使用KMP算法解决这个问题的时间复杂度为O(N),默认M<N,如M>N,则不用匹配。

 

介绍KMP算法之前,我们来先介绍最长前缀和最长后缀:

例如  str="abcabcd"    求字符‘d’ 的最长前缀和最长后缀的匹配长度,先求字符‘d‘的前缀数组和后缀数组

d的前缀数组:“a”,"ab","abc","abca","abcab"  前缀数组中的字符串中不包含最后一个字符(即‘d’的前一个字符‘c‘)

d的后缀数组:"c", "bc","abc","cabc","bcabc"   后缀数组中的字符串中不包含第一个字符(即首字符‘a’)

从上面可以看出字符‘d’ 的最长前缀和最长后缀的匹配长度为3,即均为"abc",则我们定义‘d‘字符上的位置(属于位置6)的next[6]=3;

依次我们来求next[0],next[1],next[2],next[3],next[4],next[5].

0位置和1位置上不存在前缀和后缀,因此我们人为定义next[0]=-1,next[1]=0;

针对位置2(字符‘c’),我们可以看出,前缀数组为:"a",后缀数组为:"b",显然最长前缀和最长后缀的匹配长度为0,故next[2]=0;

针对位置3(字符‘a’) ,我们可以看出,前缀数组为:"a","ab",后缀数组为:"c","bc",显然最长前缀和最长后缀的匹配长度为0,故next[3]=0;

针对位置4(字符‘b’) ,我们可以看出,前缀数组为:"a","ab","abc",后缀数组为:"a","ca","bca",显然最长前缀和最长后缀的匹配长度为1,故next[4]=1;

针对位置5(字符‘c’) ,我们可以看出,前缀数组为:"a","ab","abc","abca",后缀数组为:"b","ab","cab","bcab",显然最长前缀和最长后缀的匹配长度为2,故next[5]=2;

上面解释的KMP算法中next数组的来源,那么我们怎么用比较优秀的算法来求这个next数组呢?

1)求next[i],需要根据next[i-1]来求,首先,定义cn = next[i-1],判断str[i-1]是否与str[cn]相等,

    如例子1:str[i-1]==str[cn]=‘c’,则next[i]=cn+1=3,此处cn=next[i-1]=2

   如例子2和3:str[i-1]!=str[cn],即‘c‘!=‘t‘ , ‘c‘!=‘a‘,此处cn =next[i-1]=4,此时将cn前跳,即cn = next[4]=2

                     继续比较str[i-1]是否等于str[cn],如例子2和3所示,例子3显示‘a’==‘a‘,故针对例子3,next[i]=cn+1=3,

                                                                                           例子2显示‘a’!=t,故cn继续前跳,cn=next[cn]=next[2]=0;

               例子2继续比较str[i-1]是否等于str[cn],此时‘a’!=‘t‘,cn继续前跳,发现已不能往前跳,故next[i]=0

                     

技术分享图片

 求next数组代码如下所示:

//构造str2的next数组,规定next[0]=-1,next[1]=0;
    //ababcababtk  ==> ababc ababtk,经过验证,以下代码为正确
    public static int[] getNextArrays(String str) {
        char[] strs = str.toCharArray();
        int[] next = new int[str.length()];
        int cn = 0;//为前跳位置
        next[0] = -1;
        next[1] = 0;
        int i = 2;
        while (i < next.length) {
            if (strs[i - 1] == strs[cn]) {
                next[i] = cn+1;
                i++;
                cn++;
            } else if (cn > 0) {
                cn = next[cn];//往前跳
            } else {
                next[i] = 0;
                i++;
            }
        }
        return next;
    }

求完str2的next数组后,如何求的str2在str1字符串中所在的位置呢?此处是如何利用next数组进行加速处理的呢?

 

以上是关于KMP算法详解的主要内容,如果未能解决你的问题,请参考以下文章

KMP算法详解以及Java代码实现

数据结构KMP算法配图详解(超详细)

疫情封校在宿舍学习KMP算法详解(next数组详解)附例

kmp 算法详解

kmp算法详解

KMP算法详解