KMP算法

KMP算法通常用来匹配字符串,时间复杂度为O(n+m)。一般来说,我们匹配字符串,首先想到的是对源字符的每个位置,都进行匹配。但这样花费的时间过多,时间复杂度为O(n*m),虽然在实际中,也可以接近O(n+m)。KMP算法采用了一个数组来保存元素的位置,这样就可减少匹配的次数(是不是和DP有些类似呢)

下面我们来说一下,它的实现思路,假设,有两个字符串源字符串R,目标字符串T,用来保存T中每个元素位置的数组next[],设匹配从R的第i个元素开始(我们以下标1表示第一个元素),第一次查看T的第j个元素(这里为第1个)是否与i相同,若相同j++,继续匹配。若不同,令j = next[ j ],然后继续匹配,直到j = 1。若还是不同,则令i++,j++,然后继续匹配,如此循环直到超出R或T的长度。

下面说一下它是如何减少匹配次数的,关键就在于next[],当我们发现有不匹配元素,不要急着将i的还原为匹配前的下一个,而是偷一下懒,先看一下T中是否有比当前j小,且与当前位置“等价”的位置,这里的意思是在T中存在着两段小数组t1,t2。t1与t2相等,且t1以T的第一个元素为开头,t2以“T的最后一个元素”为结尾(实际上以第j-1个元素结尾)。若存在t1,t2,则j = t1.length+1,即让t1后的第一个元素与R中的第i个元素进行比较。若相同,则比较下一元素,若不同,则再次进行此操作。这好像将T字符串,往前推(R不动),改为将R字符串往后推(T不动)。实际上,将T往后推,节省了大量的比较操作。而我们在next[]中存储的就是每个T元素的t1.length+1;这样当在第j个元素处不相同时,就可以直接从数组拿来使用。

下面我们再来说一下next[]的实现。首先,我们让它的长度为T.length+1,因为我们要以下标1表示第一个元素。并令next[1]=0;这样如果j==0,我们就知道T的元素到第一个了,应该让j=1,i++;进行下一个元素的匹配。

next[ j ] = k,{ k=Max(t1'.length,t1''.length) }。因为符合条件的t1可能有好多个,所以我们选那个长度最长的t1。

next[ j ]=1。若j之前的那段数组没有符合条件的,则令其为1。下面开始正式说明如何建立next[]

建立next[],其实也和比较两个字符串类似。我们可以设两个相同的T字符串,源字符串T1,匹配字符串T2,在开始时,使T2的第一个元素对应T1的第二个元素(否则没法比较,因为它们都相同).......(Talk is cheap ,show me the code --Linus)

/**
	 * 
	 * @param str 对str字符串建立next数组
	 * @param next
	 */
	private static void next(String str,int []next){
		
		next[1]=0;
		/*
		 * i指示T1的位置
		 * j指示T2的位置
		 * same表示i之前相同元素的最大个数,即相同小数组的最大长度(不包括i)
		 * 用same为next[]设值
		 */
		int i=2,j=1,len=next.length,same=0;
		char a=0,b=0;
		while(i<len){
			a=str.charAt(i-1);
			b=str.charAt(j-1);
			//表示在第一次到大该元素时,就为其设置值,因为推R的原因,可能会多次比较,
			//其值为i之前相同元素的个数+1
			if(next[i]==0){
				next[i]=same+1;
			}
			//两个元素相同,比较下一个,并令same++
			if(a==b){
				same++;
				i++;
				j++;
			}else{
				//不同向后推T2,设置新的j值,并更改same的值,改为j-1,若存在的话,其值相当于t1。length
				//不存在,即same<0说明到达第一个元素
				j=next[j];
				same=j-1;
				if(same<0){
					same=0;
				}
				//意味着到达T2的第一个元素,应该都向后移一个元素,并令same为0
				if(j==0){
					i++;
					j=1;
					same=0;
				}
			}
			
		}
		
	}


这样next[]就设置完成了,下面给出我的KMP算法:

private static int find(String pattern,String compile,int pos){
		int i=pos,j=1,len_pattern=pattern.length(),len_compile=compile.length();
		char a=0,b=0;
		while(i<len_pattern+1&&j<len_compile+1){
			a=pattern.charAt(i-1);
			b=compile.charAt(j-1);
			if(a==b){
				i++;
				j++;
			}else{
				j=next[j];
				if(j==0){
					j=1;
					i++;
				}
			}
		}
		if(i<len_pattern){
			return i-j+1;
		}else if(j==len_compile+1){
			return i-j+1;
		}else{
			return -1;
		}
		
		
	}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值