字符串匹配——KMP算法的Java实现

开始复习算法,复习到字符串这一结构时,一个经典的问题就是两个字符串的匹配问题。
比如:在主串ssdfgasdbababa中找是否存在一个asdba的子串。

传统方法——暴力匹配

用传统的方法就是暴力匹配,从主串中一个个地和子串匹配。
最坏的情况下,就是匹配到最后一步才得到结果。
其时间复杂度为O((m-n)*n),其中主串的长度为m,子串的长度为n。

KMP算法

以下为自己的理解,表达上难免有些口语化:)
KMP算法的核心思想就是:如果A==B,B!=C,那么A必然不等于C!,这样一来就省掉了比较A和C的步骤。
KMP的大致思想就是:利用一个next数组,保存子串中每个字符前面的前缀和后缀的最大相同匹配长度,为了在与主串匹配时,如果存在相同的部分,就可以通过next数组来“跳过”已经相同的部分,省掉一些匹配操作。
如果想看详细解释,推荐另一篇博客:KMP算法最浅显理解——一看就明白,不过提醒下这篇是基于C++实现的,不过原理讲的挺好的。

代码实现与理解

在KMP算法上分两部分:一部分是计算next数组,另一部分就是子串和主串匹配。其实两者实现原理是一致的。

计算next数组

首先计算子串的next数组
在这里要解释下,不同版本的KMP算法虽然原理一样,但是各自对next的值有不同的理解,在这里说明一下,next数组中:

  • 为0:代表前缀和后缀没有匹配的,同时默认子串第一个元素的next值为0,比如abc每个字符的next值都为0;
  • 为1:代表前缀和后缀有一个字符匹配的,比如aba中,最后一个字符a的next值为1;
  • 为2:代表前缀和后缀有两个字符匹配的,比如abab中,最后一个字符b的next值为2;
  • 以此类推3,4,5······
    public static int[] calNext(String str){
        int length = str.length();      //子串的长度
        int j = 0;                      //j是next数组的下标
        int[] next = new int[length];   //初始化子串
        next[0] = 0;                    //子串的第一个元素肯定没有匹配的,默认为0

        for(int q = 1; qwhile(j>0 && str.charAt(j)!=str.charAt(q)){ //如果不相等
                //j下标表示next数组的index,q下标表示子串的index
                //j>0说明前面仍有部分匹配的情况
                //注意:在next数组中next[j]永远小于或等于j的,保证当不相等的情况下,够往前回溯
                j = next[j-1];  //回溯            
            }
            if (str.charAt(q)==str.charAt(j)) {    //如果相同,则j加1
                j++;
            }
            next[q] = j;
        }
        return next;
    }

利用next数组,匹配主串和子串

KMP主方法思想和上面求next数组思想是一致的,只不过一个是串内匹配,这个是两个串之间匹配。
直接上代码:

//str为主串,dest为子串,next为上面得到的next数组
public static int kmp(String str, String dest, int[] next){

        for(int i=0,j=0;i<str.length();i++){
            while(j>0&&str.charAt(i)!=dest.charAt(j)){  //说明子串和主串当前下标的元素不匹配
                //j>0说明前面有相同的部分
                j = next[j-1];    //回溯
            }
            if(str.charAt(i)==dest.charAt(j)){
                j++;
            }
            if(j == dest.length()){    //说明匹配到了子串末端,找到匹配的地方
                return i-j+1;
            }
        }
        return -1;    //如果找不到匹配,返回-1
    }

主程序

最后就是在主程序中调用了。

public static void main(String[] args) {
        // TODO Auto-generated method stub
        String str = "ssdfgasdbababa";
        String dest = "ababa";
        int[] next =  calNext(dest);
        System.out.println(kmp(str, dest, next));
    }

总结

当时理解KMP算法还是有点绕,稍微有些难度(原谅我算法基础不咋样~)
最后说一下,KMP算法在字符串存在部分匹配才会体现它的效率,否则和一般方法区别不大。

你可能感兴趣的:(算法)