关于KMP算法,网上很多资源,就不介绍了,建议不懂的看看数据结构这本书,很详细,或者参看下面说说java实现,(整个算法流程加代码实现花了快一天时间)
首先看看一般的算法,也就是网上说的暴力(蛮力)算法,它的时间复杂度为O(n*m) m为模式串的长度,n为主串的长度
package test03;
public class test05 {
public static int volientMatch(String s, String p) {
int slen = s.length();
int plen = p.length();
int i = 0, j = 0;
char[] sarray = s.toCharArray();
char[] parray = p.toCharArray();
while (i < slen && j < plen) {
//当前字符匹配成功,则i++,j++
if (sarray[i] == parray[j]) {
i++;
j++;
} else {
//当前字符匹配不成功,则模式串的j置0,主串从目前模式串首字符对应的下一个主串字符开始继续匹配
//也就是先把主串的i回溯到目前模式串的首字符所在的位置的下一个字符(i-j+1),接下来
//把模式串的p[0]字符与主串的这个(i-j+1)处的字符比较
i = i - (j - 1);
j = 0;
}
}
//匹配成功,返回模式串p在主串s中的位置,否则返回-1
if (j == plen) {
return i - j;
} else {
return -1;
}
}
public static void main(String[] args) {
String s="abcdasdff";
String p="sdf";
int index = volientMatch(s, p);
System.out.println(index);
}
}
package test03;
public class test06 {
public static void main(String[] args) {
String source="abcadefabdf";
String pattern="abdg";
int index = KmpSearch(source, pattern);
System.out.println(index);
}
/**
*
* @Title: KmpSearch
* @Description: KMP算法
* @param @param s 主串
* @param @param p 模式串
* @param @return 模式串在主串的匹配位置
* @return int
* @author Administrator
* @date 2017-5-25
* @throws
*/
public static int KmpSearch(String s,String p){
int i=0,j=0;
char[] sarray = s.toCharArray();//把主串和模式串都转化为字符数组
char[] parray = p.toCharArray();
int slen=s.length();
int plen=p.length();
int[] next = getNext(p);//获取模式串的next数组
while(i<slen&&j<plen){
//如果j=-1或者当前字符匹配成功,则i++,j++
if(j==-1||sarray[i]==parray[j]){//注意next数组中可以取到-1的值,表示为模式串的头部,
j++;
i++;
}else{
//如果j!=-1,且当前字符匹配失败,则令i不变,j=next[j]
j=next[j];
}
}
if(j==plen){
return i-j;
}else{
return -1;
}
}
/**
*
* @Title: getNext 获取模式串的next数组
* @Description: TODO
* @param @param p
* @param @return
* @return int[]
* @author Administrator
* @date 2017-5-25
* @throws
*/
public static int [] getNext(String p){
int plen=p.length();
char[] parray = p.toCharArray();
int [] next=new int [plen];
next[0]=-1;
int k=-1,j=0;
/**
首先要明白next[j]=k是什么意思,简单概述就是:当主串s和模式串p比较时,当s[i]!=p[j]时,
(我们不采用蛮力法时)我们的主串s的s[i]字符接下来该和模式串的p[k]字符进行比较,即我们
不再是像蛮力法那样,每次把模式串右移一个字符,再回溯到主串的下一个字符进行比较。采用KMP
算法,我们不需要回溯主串,而是直接把模式串右移j-k(j-next[j])个位置,进行下次比较。
也就是说当s[i]!=p[j]的时候,接下来我们让s[i]和p[k](p[next[j]])进行比较。
那么我们我们应该怎么求解next数组呢?已知next[j]=k,我们可以采用递归的方式求next[j+1]的值
注意next[j]=k意味着p[0]p[1]p[2]....p[k-1]=p[j-k]....p[j-1],=左右各有k个值
所以当求next[j+1]的时候,就需要比较p[0]p[1]p[2]....p[k-1]p[k]=p[j-k]....p[j-1]p[j]
又因为p[0]p[1]p[2]....p[k-1]=p[j-k]....p[j-1]相等,所以只需要比较p[k]和p[j]
假如p[k]=p[j],则next[j+1]=k+1,
假如p[k]!=p[j],令k=next[k],此时再比较p[j]与p[k],假如如果不相等,则继续递归前缀索引,
令 k=next[k],继续判断,直至k=-1(即k=next[0])或者p[j]=p[k]为止。
*/
while(j<plen-1){//对于数组next长度为plen所以当一个循环不满足条件时,j=plen-1,刚好是next数组的最后一个。
if(k==-1||parray[j]==parray[k]){//因为不可能p[0]和p[-1]比较,所以用k==-1
++k;
++j;
next[j]=k;//next[j+1]=k+1
}else{
k=next[k];
}
}
return next;
}
}
其中求next数组的时间复杂度为o(m)其中m为模式串的长度,匹配的时间复杂度为o(n),n为主串的长度,所以总的时间复杂度为o(m+n)。