数据结构——KMP算法

1.什么是模式匹配

 

自己看看百度百科,模式匹配、简单模式匹配

https://baike.baidu.com/item/%E6%A8%A1%E5%BC%8F%E5%8C%B9%E9%85%8D/1258334?fr=aladdin

 

2.模式匹配的KMP算法的优点

 

        KMP算法是对简单模式匹配算法的改进,简单模式匹配算法,每次模式串(子串)匹配不同时,模式串都要从头开始匹配,理论上时间复杂度为O(m*n),效率不高,KMP算法当某一个字符匹配不同时是从特定的位置开始的,该位置前所有字符都一一与主串不匹配位置的字符匹配,时间复杂度为O(m+n),大大提高效率。

        最大优点是匹配时,主串索引不需要回溯,这样当数据量大时,可以分段操作,先读入内存一部分进行匹配,完成之后可写回外存,确保在发生不匹配时不需要将之前写回外存的部分再次读入,减少了I/O操作

注:为了方便理解,串的字符数组都是从下标为1开始存储字符,字符数组第一个元素存放串的长度

 

如果应用中是从下标0开始存储字符,则相应next数组值相差1

 

3.KMP算法

 

KMP算法由两部分实现第一步是实现模式串的getNext()函数,得到next数组

next[j]表示模式串第j个字符不匹配时,应从next[j[处的字符重新与主串比较

 

/**
 * @author 芜情
 * KMP算法,这里直接从0开始,没按照书上的从索引1开始存储
 */
public class MyString {
	private char[] s;
	//赋值
	public MyString(char[] chs){
		if(chs == null){
			s = null;
			return;
		}
		if(s != null){
			s = null;//释放原串空间
		}
		s = new char[chs.length+1];
		s[0] = (char) chs.length;
		for(int i = 1; i < s.length; i++){
			s[i] = chs[i-1];
		}
	}
	//模式匹配
	public int indexOf(MyString T){
		char[] t = T.s;
		int[] next = getNext(t);
		int i = 1, j = 1;
		while(i <= s[0] && j <= t[0]){
			if(j == 0 || s[i] == t[j]){
				++i;
				++j;
			}else{
				j = next[j];
			}
		}
		if(j > t[0]){
			return i - t[0];
		}
		return -1;//匹配不成功
	}
	//next()函数
	//next[j]表示在第j个字符不匹配时,模式串重新匹配的位置
	//next[j] = k,说明对模式串T有T[1] = T[j-k+1],...,T[k-1] = T[j-1](共k个)
	public int[] getNext(char[] chs){
		int[] next = new int[chs.length];
		next[1] = 0;//第1个字符不匹配,则模式串右移,模式串第1个字符跟主串下一个字符比较
		//分析时是倒着推,写程序得正着来,因为要求next[i+1]得先知道next[i]的值
		int i = 1, j=next[i];
		while(i < chs[0]){
			//j=0代表递归到next[1]了,此时表达式形式上同p(j)=p(i)时的情况
			if(j == 0 || chs[i] == chs[j]){
				//此种情况next[i+1] = next[next[i]] + 1
				//即 next[i+1] = j + 1
				++i;
				++j;
				next[i] = j;//p(j)=p(i)时的表达式next[i+1]=next[i]+1
			}else{
				j = next[j];//继续递归
			}
		}
		return next;
	}
}

4.KMP算法的改进——nextVal()函数

 

//改进的next()函数nextVal()
	//有事重新定位匹配位置时,刚好第一个就不匹配,又得调用一次next()函数
	//nextVal()函数就是一次搞定,保证调用一次nextVal()起码匹配位置往后移一个
	public int[] getNextVal(char[] chs){
		int[] nextVal = new int[chs.length];
		nextVal[1] = 0;
		int i = 1, j=nextVal[i];
		while(i < chs[0]){
			if(j == 0 || chs[i] == chs[j]){
				++i;
				++j;
				if(chs[i] != chs[j]){
					nextVal[i] = j;
				}else{
					nextVal[i] = nextVal[j];
				}
			}else{
				j = nextVal[j];
			}
		}
		return nextVal;
	}

 


不管是next数组还是nextVal数组,索引都是从1开始,它代表是串的第几个元素,而数组的值则是根据实际情况,看设定的是从0开始还是从1开始

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值