KMP-字符串快速匹配算法

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/only_on_one/article/details/52262926

之前一直保持在word文档中记录总结,最近发现C博客是个分享的好地方,记录自己学习总结的同时,也可以把总结拿出来分享,万一能帮到别人呢^_^
花了两天,把KMP算法搞懂了,此篇旨在用通俗易懂的语言讲算法实现过程,但看此篇之前最好对朴素字符串匹配动手写过代码。好了,下面进入正题。
朴素字符串匹配算法时间复杂度为O(n*m),n/m分别为主串/子串长度,而KMP算法的时间复杂度为O(n+m)。当主串或子串非常长时,这个时间提升的就非常明显了。
KMP算法思路:主串指针i不回溯,只回溯子串指针j,指针j回溯到哪,就看nextVal[i]的值了。所以该算法关键是求nextVal[]数组。其实nextVal[]是在next[]数组的基础上求得的,它改进了使用next[]的不足。下面一步步来讲。
1、next[]数组求解
这里需要先将所给子串化成字符数组char[] p,从1开始放子串字符(为什么从1开始,为了数组计算方便),所以p.length=子串长度+1,next[]长度等于p的长度。
抽象层面来说,next[i]=子串p[1,2,…,i-1]中前缀子串与后缀子串相等时的最大长度+1。具体利用代码自动求解,可以利用next[i]与next[i+1]的递推关系,如下图所示。
这里写图片描述
假设已知next[i]=j,即p[1,…,k-1]=p[i-k+1,…,i-1],这时比较p[i]和p[j]:
(1)如果p[i]=p[j]=p[next[i]],则有p[1,…,j]=p[i-j+1,…,i],前缀子串与后缀子串相等的最大长度为j+1,即next[i+1]=j+1=next[i]+1.
(2)如果p[i] != p[j],则将j回溯到next[i],即j=next[i],然后继续比较p[i]和p[j].
(3)这里注意特殊情况:j回溯到0后,即到了上图最下面j==0的情况,因为p[0]里面为非子串字符,无意义,这时就要指针i、j都加1,加过后next[i]=j,再继续比较p[i]和p[j]。
(4)初始条件next[1]=0,next[2]=1。
下面是Java版求next[]代码:

    public static int[] getNext(char[] p){
        int i=2,j=1;
        int[] next = new int[p.length];
        next[1]=0;
        next[2]=1;
        while(i<next.length-1){
            if (j==0 || p[i]==p[j]){
                i++;
                j++;
                next[i]=j;
            }
            else j=next[j];
        }
        return next;
    }
2、nextVal[]求解
为什么要引入nextVal[]数组?因为next[]有缺陷,以子串"ababaaaba"为例。

这里写图片描述
当j=5时,如果子串p[5]!=主串s[i],那么j要回溯到j=next[5]=3,接着,比较p[3]与s[i],因为p[3]==p[5],所以p[3]!=s[i]。可见,这一步是多余的,我们可以直接j=next[3],以此类推。这就是nextVal[]的由来,对求next[]的代码稍加改动,即可求解nextVal[],代码如下。

    public static int[] getNextVal(char[] p){
        int i=1,j=0;
        int[] next = new int[p.length];
        int[] nextVal = new int[p.length];
        next[2]=1;
        nextVal[1]=0;
        while(i<next.length-1){
            if (j==0 || p[i]==p[j]){
                i++;
                j++;
                next[i]=j;
                if(p[i]==p[j]) nextVal[i]=nextVal[j];
                else nextVal[i]=j;
            }
            else j=next[j];
        }
        return nextVal;
    }

3、匹配主串
返回子串在主串中首次出现的位置,str是主串,pc是子串。

    public static int KMP(String str,String pc) {
        int[] nextVal = getNextVal(pc);
        str = '0' + str;  //主串
        pc = '0' + pc;  //子串
        char[] s = str.toCharArray();
        char[] p = pc.toCharArray();

        int i=1,j=0;
        while(i<s.length && j<p.length){
            if(j==0 || s[i]==p[j]){
                i++;
                j++;
            }
            else j=nextVal[j];
        }
        if(j==p.length) return i-p.length+1;
        else return -1; //Unfound
    }
展开阅读全文

没有更多推荐了,返回首页