zoukankan      html  css  js  c++  java
  • 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数组进行加速处理的呢?

    public static int getIndexOfString(String str1, String str2) {
            int next[] = getNextArrays(str2);
            char[] str1s = str1.toCharArray();
            char[] str2s = str2.toCharArray();
            int j = 0;//代表str1的索引
            int i = 0;//代表str2的索引
            while (i < str2.length() && j < str1.length()) {
                if (str2s[i] != str1s[j]&&j>-1) {//满足j>=0的要求
                    //当next[i]=0时,即i前面并没有最长前缀与最长后缀的匹配长度,此时从当前j位置开始比较起
                    //此处当next[i]=-1,j=0时,自动满足j=j+1。
                    j = j - next[i];//后退next[i]个字符,,
                    i = 0;//重新开始比较
                } else {
                    i++;
                    j++;
                }
            }
            if (i == str2.length()) {//说明全部比较完,且在str1中找到str2的字符串
                return j - i;
            } else {
                return -1;
            }
        }

  • 相关阅读:
    简述JavaScript中 同步与异步,阻塞与非阻塞
    浅谈 JavaScript 中 (for循环) 的几种写法及其性能问题
    简述vue下高德地图的一些简单功能的使用(定位,添加marker)
    vuex中mutations与actions的使用及区别
    项目中 vue与高德地图一起使用 (一)
    css3动画,requestAnimationFrame动画与canvas图形
    setTimeout 在 js 加载前的问题探究
    面试答案
    css实现垂直居中的几种常见方法
    align-conten和align-items之间的区别
  • 原文地址:https://www.cnblogs.com/zjf-293916/p/9221909.html
Copyright © 2011-2022 走看看