• KMP算法详解


    博文中例子来自于 http://www.cnblogs.com/yjiyjige/p/3263858.html

    算法的来历就不仔细说明了,是相对于暴力破解匹配算法来的,其思路如下图

    从以上思路可以看出,当被匹配串从k开始比起一直比较到位置i,模式串到了j ,匹配失败了,i向后移动到 k+1的位置 , j 移到0。

    而KMP算法改善了暴力破解的效率,其基本思路在是:可以根据模式串的组成规则,从而基本只要移动 j的位置 (当第一个就不匹配的时候,i 也会向后移动一个位置),其思路用图片描述如下

     

    接下来,算法的关键部分就是要根据模式串的组成规则,计算出模式串每个位置 j 移动的next(k)值(即当匹配到模式串位置 j 匹配失败的时候,j 应该回溯到的位置) 

    public   int[] getNext2(String ps)
        {
            char[] strKey = ps.toCharArray();
            int[] next = new int[strKey.length];
    
            // 初始条件
            int j = 0;
            int k = -1;
            next[0] = -1;
         
            // 根据已知的前j位推测第j+1位
            while (j < strKey.length - 1)
            {
                if (k == -1 || strKey[j] == strKey[k])
                {
                    next[++j] = ++k;  // ----> j++ ; k++ next[j] = k; 由此也可以看出求得是第j+1位的next
                }
                else
                {    // 例子:
                    //index:   0 1 2 3 4 5 6 7 8 
                    //strKey:  a b a d a b a s s
                    //next:    -1 0 0 1 0 1 2 3 0
                    // 当strKey[3]和strKey[7]比较的时候,不相等,这时候应该比较 strKey[next[3]](next[3]=1)和strKey[7],可以看出
                    // 当strKey[3]、strKey[7]和 strKey[next[3]]前面那个元素都是相同的,都为a。由于strKey[1]=b!=s,所以继续是比较next[1]=0,再进一步变成-1进入判断
                    // 所以这一步可以理解为 X={a b a d}已经不可能成为模式串 (0~8-1)位的真子集,这时候,可以继续在X中寻找真子集,可能满足要求,若将 strKey[7]改成b,即:
                    //index:   0 1 2 3 4 5 6 7 8 
                    //strKey:  a b a d a b a b s
                    //next:    -1 0 0 1 0 1 2 3 2
                    //next[8]最终的结果就为1了
                    k = next[k];
                }
            }
    
             return next;
        }

    这种求next的方法是有缺陷的,若模式从都是sssssssssssb这样的字符串,next = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ,b之前的任何一个s不匹配完全没有必要再移动模式串 j 的位置,移动后必须是不匹配的

    发生问题的原因在于strKey[j] == strKey[ next[j] ]。   该串的next优化后为 [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10] (负数表现被匹配串 i 后移)

    (1)  strKey[j] == strKey[ next[j] ] 当 strKey[j] 不匹配时,决定不能在移动到next[j] ,在next[j] 必然失匹配 C != B

    主串      A B A C B C D H 

    strKey  A B A B 

    next          -1 0  0  1

     (2) strKey[j] != strKey[ next[j] ] 当 strKey[j] 不匹配时,移动在next[j] ,但不一定在next[j] 就会匹配 C != B

    主串      A B A C B C D H 

    strKey  A B A D

     next         -1 0  0  1

     (3 当 strKey[j] 不匹配时,只有strKey[j] != strKey[ next[j] ] ,主串才可能匹配 B == B

    主串      A B A B A C 

    strKey  A B A C

     next         -1 0  0  1

    所以要过滤掉 strKey[ j ] == strKey[ next[j] ]  的情况,得到以下的第二种算法

    public   int[] getNext3(String ps)
        {
            char[] strKey = ps.toCharArray();
            int[] next = new int[strKey.length];
    
            // 初始条件
            int j = 0;
            int k = -1;
            next[0] = -1;
         
            // 根据已知的前j位推测第j+1位
            while (j < strKey.length - 1)
            {
                if (k == -1 || strKey[j] == strKey[k])
                {
                    // 如果str[j + 1] == str[k + 1],回退后仍然失配,所以要继续回退
                    if (strKey[j + 1] == strKey[k + 1])
                    {
                        next[++j] = next[++k];
                    }
                    else
                    {
                        next[++j] = ++k;
                    }
                }
                else
                {
                    k = next[k];
                }
            }
    
             return next;
        }

    接下来贴上完整kmp算法

    private int kmpMatch(String str , String pattern ){
            int next[] = getNext3(str);
            int i=0,j=0;
            char[] strChar = str.toCharArray();
            char[] patternChar = pattern.toCharArray();
            while(i<str.length()&&j<pattern.length()){
                if(strChar[i] == patternChar[j]){
                    i++;
                    j++;
                }else{
                    if(next[j]>=0){
                        j = next[j];
                    }else{
                        // 当next[j]<0 即 j为-1,这时j要移动到0的位置,i要往后移动一位,这部分代码可以和上面的合并
                        i++;
                        j = 0;
                    }
                }
            }
            
            if( j ==pattern.length()){
                return i-j;
            }else{
                return -1;
            }
            
            
        }
  • 相关阅读:
    JDK1.0-缓冲流
    笔试错误1
    JVM 垃圾收集(转)
    Trie树和后缀树(转,简化)
    海量数据处理(转,简化)
    Struts2 内核之我见(转) -(主要是拦截器链和过滤链介绍和源码及其设计模式)
    phpize增加php模块
    Ubuntu下SVN安装和配置
    Linux下SVN配置hook经验总结
    Kruakal 算法——练习总结
  • 原文地址:https://www.cnblogs.com/liujiaa/p/7995616.html
Copyright © 2020-2023  润新知