Kmp算法

KMP算法:KMP算法是由Knuth,Morris,Pratt三位学者研究出的模式匹配算法,大大的避免了重复遍历的情况。

算法目的:确定主串中所含子串第一次出现的位置。

 

在这之前呢,有一个BF算法 (~暴力匹配~)。>_<

BF算法设计思想:

1,将主串的第pos个字符和模式的第1个字符比较,

    2,若相等,继续逐个比较后续字符;

3,若不等,从主串的下一字符(pos+1)起,重新与第一个字符比较。

4,直到主串的一个连续子串字符序列与模式相等 。返回值为S中与T匹配的子序列第一个字符的序号,即匹配成功。

5,否则,匹配失败 。

public static int BF(String str,String sub,int pos){
		
		//1.判断pos合法性。
		if(pos < 0 || pos > str.length()){
			return -1;
		}
		//2.开始查找。
		int i = pos;
		int j = 0;
	
		//3.遍历主串和子串。
		while(i <str.length() && j <sub.length()){
			if(str.charAt(i) == sub.charAt(j)){
				i++;
				j++;
			}else{
				i = i-j+1;
				j = 0;
			}
		}
		
		//4.那种情况下表示找到
			if(j >= sub.length()){
				return i-j;
			}else{
				return -1;
			}
	}

 

(回溯就是坚持条条大路通罗马的决心,然后遇到挫折就回到跌倒的地方重新爬起来,继续往前,这种思想是好的,但效率非常低)

 

——————————————————————————————————

 

—_—  所以啦,这个效率这么低。就肯定会有大神开始苦心研究。。。。终于有了现在的KMP算法啦^-^

KMP算法的核心也就是避免不必要的回溯

 

 

接下来,我们来认真分析一下KMP算法啦

1>


  

 

由上图可知两个字符串是在第五个失配

i1=j1   

i2=j2    j1 !=j2     所以----->j1 != i2

i3=j3    j1 !=j3     所以----->j1 !=i3

...

因此失配后j回溯到j1,但是i不需要回溯


  

 

2>


  

 

由上图可知两个字符串是在第三个失配

i1=j1  

i2=j2   j1=j2   所以----->j1=i2

因此失配后j回溯到j2,但是i不需要回溯


  

 

 

3>

 

由上图可知两个字符串是在第六个失配

 

i1=j1

i2=j2

i4=j4  j1=j4 ----->j1=i4

i5=j5  j2=j5------>j2=i5

因此失配后j回溯到j3,但是i不需要回溯


  

 

4>


  

 

i4=j1

i5=j2   j1=j2  所以------->i5=j1

i6=j3   j2=j3  所以-------->i6=j2

i7=j4   j3=j4   所以------->i7=j3

因此失配后j回溯到j4,但是i不需要回溯


  

 

 

next[]数组:当模式匹配串T失配的时候,next[]数组对应的元素应该用串T的哪个元素进行下一轮的匹配。

 

由前面的规律总结出next[]数组

  每个元素的next数组为(模式串中前缀和后缀相同的个数加一)(ps:下标从0开始的next数组就是前缀和后缀相同的个数)

子串T   T0......Tk-1 = Tj-k.......Tj-1

怎样得到next数组

首先假设:next[i] = k 成立,就有  p0.....pk-1 = px.....pi-1    得到 p0....pk-1 = pi-k...pi-1 --------------->再假设如果pk = pi;我们可以得到p0.....pk = pi-k....pi;那这个就是next[i+1] = k+1;   

假设  pk!= pi

就继续回溯

k = next[k];

	public static void getNext(String sub,int[] next){
		next[0] = -1;
		next[1] = 0;
		int k = 0;
		int i = 2;
		if(k == -1 || sub.charAt(k) == sub.charAt(i-1)){

			next[i] = k+1;
			k++;
			i++;
		}else{
			k = next[k];
		}
	}
	public static int kmp(String str,String sub){
		int i = 0;
		int j = 0;
		int[] next = new int[sub.length()];
		getNext(sub,next);
		while(j < sub.length()){
			if(j == -1 || sub.charAt(j) == str.charAt(i)){
				i++;
				j++;
			}else{
				j = next[j];
			}
		}
		if(j >= sub.length()){
			return i-j;
		}else{
			return -1;
		}
		
	}

后来呢,就有人发现KMP算法有缺陷


这种情况下尽管是遵循了KMP算法,这样的效率还是很低。

(因为i5和j5失配,i1,i2,i3,i4都等于i5,所以我们一眼就能看出i1,i2,i3,i4,也都是失配的。)

所以我们只需要在next[]数组的计算中加上判断,如果它的下一个元素与前缀元素相等,那么它应该回溯到前缀元素的next[]数组值那儿去。

public static void getNextval(String sub,int[] next){
		next[0] = -1;
		next[1] = 0;
		int k = 0;
		int i = 2;
		if(k == -1 || sub.charAt(k) == sub.charAt(i-1)){
			if(sub.charAt(k) == sub.charAt(i-1)){
				next[i] = next[k];
			}else{
				next[i] = k+1;
			}
			k++;
			i++;
		}else{
			k = next[k];
		}
	}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值