kmp算法分析

kmp算法真的是很难理解的一枚算法,前前后后看了很多篇博客才有那么理解。

首先我们看看在kmp算法中有哪些对象,一是原始字符串O,二是待匹配字符串(P)。

正常的情况下我们需要挨个匹配,也就是说对1<i<O.length(), 我们都需要匹配,还可以是1<i<(O.length() - P.length())。这种匹配的时间复杂度为(O.length() - P.length()) * P.length(),显然效率不够高!

kmp算法的出世就是解决效率问题的。

kmp要拿什么开刀去解决效率问题呢?通过对字符串进行分析,我们发现对于P来说,存在两部分,已匹配和未匹配,我们是否可以使用已匹配的字符串信息辅助提高效率呢?答案是肯定的。

我们以具体例子为例去分析, O=ABCDABCABCDABDEF, P=ABCDABD,指针i指向O且从头扫描到O.length() - P.length(),指针j指向P。

一开始,i、j同步位移,直到i=j=6,C!=D,这时候需要调整j的位置。对P进行分析,有两个AB字符串,在ABCDAB已经匹配成功,而D未匹配成功的情况下,匹配成功的字符串没有作用了,我们正常情况下需要将P右移一个单位继续匹配,但是我们看到ABCDAB这个字符串有特点,我们只需将P移动四个单位就可以了。这里的匹配成功的字符串是ABCDAB,具有特殊性,如果是“ABCDEF”呢?那就是直接移动整个字符串的长度的单位了。

通过上述分析,我们可以得知,可以利用对已匹配成功字符串进行分析加速移动过程,从而降低时间复杂度。那么怎样确定移动步数呢?

已匹配移动位数
ABCDAB4=(6-2)
ABCDEF6=(6-0)
ABCABC3=(6-3)
ABCDEA5=(6-1)

可以看出,我们要找的就是已匹配字符串前后一致的长度,比如对于ABCDAB,前面是AB,后面是AB,对于B来说前后一致长度为2。

根据以上分析可知我们需要提前计算出待匹配字符串每个子串的前后一致长度。比如如下

这里写图片描述

我们给出代码

package trick;

import java.util.Arrays;

public class KMP {

    public static void main(String args[]) {

        KmpMatch kmp = new KmpMatch();
        String a = "ABCDABCABCDABDEF";
        String b = "ABABAB";
        System.out.println(kmp.match(a, b));
    }

    static class KmpMatch {

        public boolean match(String a, String b) {

            int[] next = makeNext(b);
            System.out.println(Arrays.toString(next));
            int j = 0;  
            for (int i = 0; i < a.length() && j < b.length(); i++) { 

                while (j > 0 && a.charAt(i) != b.charAt(j))   j = next[j-1]; 
                if (a.charAt(i) == b.charAt(j))  j++;  
                if (j == b.length()) {  
                    System.out.println("find at position " + (i - j));  
                    System.out.println(a.subSequence(i - j + 1, i + 1));  
                    return true; 
                }  
            }  
            return false;
        }

        private int[] makeNext(String b) {

            int len = b.length();
            int[] c = new int[len];
            int q,p;
            for(q=1, p=0; q < len; ++q) {

                while(p>0 && b.charAt(p) != b.charAt(q)) {

                    p = c[p-1];
                }
                if(b.charAt(p) == b.charAt(q)) {

                    p++;
                }
                c[q] = p;
            }
            return c;
        }
    }
}

我们着重分析makeNext的代码。q是从1开始递增的指针,p是从0开始在0-q之间移动的指针。该段代码计算的是q所对应的值的next数值, p从q值往回回溯直到p,q对应的字符相等,以此来计算next[q]。匹配算法就不介绍了,比较简单。

参考内容:
1. http://www.cnblogs.com/c-cloud/p/3224788.html
2. http://jakeboxer.com/blog/2009/12/13/the-knuth-morris-pratt-algorithm-in-my-own-words/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值