单模式匹配算法-KMP算法

本文深入探讨了KMP算法在单模式匹配中的应用,包括算法原理、复杂度优化及实际代码实现。通过构建next数组辅助定位模式串滑动位置,实现高效匹配,避免不必要的回溯操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    模式匹配,一般分为单模式匹配和多模式匹配。当然,通常都是指在字符序列中的匹配问题。   

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

    本文展示的是单模式匹配中的经典算法--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 ?有时间的朋友可以实现一下,反正算法都是一样的嘛。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值