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

来源:互联网 发布:linux arp防火墙关闭 编辑:程序博客网 时间:2024/06/04 21:01

开始复习算法,复习到字符串这一结构时,一个经典的问题就是两个字符串的匹配问题。
比如:在主串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; q<length; q++){            while(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算法在字符串存在部分匹配才会体现它的效率,否则和一般方法区别不大。

原创粉丝点击