您的位置:首页 > 其它

单模式匹配算法-KMP算法

2012-07-22 01:09 309 查看
模式匹配,一般分为单模式匹配和多模式匹配。当然,通常都是指在字符序列中的匹配问题。

单模式匹配,对一个较长的字符序列,调用一次算法只匹配一个模式串。

本文展示的是单模式匹配中的经典算法--KMP算法。

KMP算法是一种高效的模式匹配算法,复杂度可以达到O(m+n),而普通模式匹配算法的复杂度为O(m*n)。

在普通匹配算法中子串与模式串都需要回溯,但这些回溯不是必要的。因为当某一位发生失配时,可以根据已匹配的结果进行判断。这个时候,会产生一个问题:当模式串中的第k位与主串的第i位比较时发生不匹配时,需要将模式串向右滑动到哪里继续与主串的第i位进行比较?如果解决了这个问题,就避免了不必要的主串回溯,减少了模式串回溯的位数,从而使算法复杂度提升到O(m+n)。

KMP算法通过分析模式串,构建next数组,来辅助定位每次模式串向右滑动的位置。具体怎样理解,网上解释的文章非常多,大家可以百度一下,你就知道了。

下面展示一下具体实现的Java代码,供大家讨论。

package houlei.support.matcher;

/**
* 单模式匹配算法(KMP算法)的匹配器。匹配字符串。
* <p>
* 创建时间: 2012-7-22 上午00:52:49
* </p>
* @author 侯磊
* @since 1.0
* @version 1.0
*/
public class KMPStringMatcher {

/**
* 当模式串匹配成功时,回调该接口方法。
*/
public interface StringHandler{
void onMatch(int index,String str,String pattern);
}

static int[] getNext(char[] pattern){
int next [] = new int[pattern.length];
next[0] = -1;
int j=0,k=-1;
while(j<pattern.length-1){
if(k==-1 || pattern[j]==pattern[k]){
j++;k++;
next[j] = k;
}else{
k = next[k];
}
}
return next;
}

/**
* 字符数组的匹配。
* @param data 待匹配的数据
* @param pattern 模式串的字符数组
* @return 失配返回<code>-1</code>,首次匹配成功时,返回模式串的位置。
*/
public static int match(char[] data,char[] pattern){
int[]next = getNext(pattern);
for(int i=0,j=0;i<data.length;){
if(j==-1 || data[i]==pattern[j]){
i++;j++;
}else{
j = next[j];
}
if(j==pattern.length){
return i-pattern.length;
}
}
return -1;
}
/**
* 字符串的匹配。
* @param data 待匹配的数据
* @param pattern 模式串
* @return 失配返回<code>-1</code>,首次匹配成功时,返回模式串的位置。
*/
public static int match(String data,String pattern){
return match(data.toCharArray(), pattern.toCharArray());
}

/**
* 字符串的匹配。当模式串匹配成功时,回调{@link StringHandler#onMatch(int, String, String)}接口方法。
* @param data 待匹配的数据
* @param pattern 模式串
* @param handler 处理器对象
*/
public static void match(String data, String pattern, StringHandler handler) {
char[] string = data.toCharArray();
char[] pChars = pattern.toCharArray();
int[]next = getNext(pChars);
for(int i=0,j=0;i<string.length;){
if(j==-1 || string[i]==pChars[j]){
i++;j++;
}else{
j = next[j];
}
if(j==pChars.length){
handler.onMatch(i-pChars.length, data, pattern);
j = next[--j];--i;
}
}
}

}


当然了,对于一次匹配一个模式串,并且,匹配过程中,字符序列不必回退,模式串有限回退,上述算法还是很可行的。

用例的代码,我没有写上来,因为,根据意思,应该很好理解我的用意。对于一个字符串中,如果只需要首次匹配成功的位置,那就不需要Handler了。但是,如果一个字符序列存在多个模式串在里面,那你就要使用Handler了,每次匹配成功的时候,算法都会回调里面的onMatch方法。你只要实现这个StringHandler接口就可以了。

之后是字节序列的模式匹配,虽然用的不多,但是,算法还是一样滴。

package houlei.support.matcher;

public class KMPBytesMatcher {

public interface BytesHandler{
void onMatch(int index,byte[]data,byte[]pattern);
}

static int[] getNext(byte[] pattern){
int next [] = new int[pattern.length];
next[0] = -1;
int j=0,k=-1;
while(j<pattern.length-1){
if(k==-1 || pattern[j]==pattern[k]){
j++;k++;
next[j] = k;
}else{
k = next[k];
}
}
return next;
}

public static int match(byte[] data,byte[] pattern){
int[]next = getNext(pattern);
for(int i=0,j=0;i<data.length;){
if(j==-1 || data[i]==pattern[j]){
i++;j++;
}else{
j = next[j];
}
if(j==pattern.length){
return i-pattern.length;
}
}
return -1;
}

public static void match(byte[] data,byte[] pattern, BytesHandler handler){
int[]next = getNext(pattern);
for(int i=0,j=0;i<data.length;){
if(j==-1 || data[i]==pattern[j]){
i++;j++;
}else{
j = next[j];
}
if(j==pattern.length){
handler.onMatch(i-pattern.length, data, pattern);
j = next[--j];--i;
}
}
}

}


是不是有些启发?可以再写写Reader的Matcher,或者InputStream的Matcher ?有时间的朋友可以实现一下,反正算法都是一样的嘛。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: