KMP 模式匹配算法

穿的模式匹配  :  字符串的定位工作

从 T="goodgoogle"中找到T="google"子串的位置。

朴素的模式匹配算法

主串的每一个字符作为子串开头,与要匹配的字符串进行匹配。对主串做大循环 i,字符T做小循环 j,直到匹配成功或者全部遍历结束。S[i] != S[j]  指针回退,重新匹配。 i = i-j+1  (回到档次匹配首位的下一个字符) j=0; 

 

暴力匹配的思路,假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置,则有:

  • 如果当前字符匹配成功(即S[i] == P[j]),则i++,j++,继续匹配下一个字符;
  • 如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0。相当于每次匹配失败时,i 回溯,j 被置为0。


KMP模式匹配算法 

大话数据结构是从T[1]开始的,然后一直晕着不知道该怎么办i , j 的值,会出现死循环。正解:初始化 i = -1,j = 0;感谢

https://www.jianshu.com/p/e2bd1ee482c3 的提示。

一切与书上就一一致了。注意产生index 的函数 跟 匹配 主副子串的 函数结构的相似性,但是并没有逻辑的相似性。

这里要深入理解 前缀、后缀  i, j 指针的意义。以及next 到底指的什么,不成功 子串指针回退的位置

package leetcode;

public class index_kmp {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		int index=0;
		String S = "abcabababaababaaaaabaeaaab";
		String T = "ababaababa";
		index = index_find(S,T,0);
		System.out.print(index);
	}
	
	//next数组记录的内容是:如果T[i]在匹配主字符串S时出现 S[m]  != T[i];那么不需要T退到T[0]
	//重新匹配(如果T中有重复子串的话) 节约匹配次数,next[i]表示 当前T[i] != S[m] 的话
	//下一次的匹配是 S[m] 与 T[next[i]];;next[i]即为下次匹配的指针回退的位置,注意数组是否为从0开始
	public static int[] next_find(String T){
		int[] next = new int[T.length()];
		next[0] = -1; //-1表示不存在相同的最大前缀和最大后缀
		int i=-1;//前缀标记
		int j=0;//后缀标记
		while ( j < T.length()-1 ){ //注意 k的取值范围
			if(i==-1 ||  T.charAt(i) == T.charAt(j) ) {
                                //当出现相同字符
				//next[j++]=i++;替换接下来三句。下次j+1位置配失败时(前提是j位置已经成功了),指针会退到 i+1 
                                //(因为i+1前的字符与j+1之前的k位一致)  处,
			
				i++;				
				j++;//继续往后遍历 看看重复子串长是多少
				next[j] = i;//下次j配失败时,指针会退到 i处,注意这里的j 与 i 都已经是本次循环+1了
			}
			else{
				i = next[i];//遍历到字符不同的地方,前缀需要向前回溯了,回溯到
			}
		}
		
		return next;
	}
	public  static  int index_find(String S,String T,int pos){
		int i = pos;//pos指向 S的开始匹配位置
		int j = 0;//指向T的匹配位置
		int[] next = next_find(T);
		while (i < S.length() && j < T.length()){
			if(j==0 || S.charAt(i)==T.charAt(j)){ 
				i++;
				j++;
			}
			else
				j = next[j];
		}
		if(j == T.length())
			return i-j;//如果循环结束时,遍历到T的最后一个字符,并且成功(j == T.length),那么返回匹配S中子串的开始位置
		else
			return -1; //返回0,不用专门考虑i是否到了最后一个字符 依旧没有匹配成功的情况,包含在 else里面了
	}


对KMP next数组求解的修改,如果 i++,j++ 之后的T[i] != T[j]  那么 j 回溯的位置就不仅仅到 next【j】也就是i 了,而是到 next【next[i]】  因为即便回溯到先前next位置,由于那个位置与当前位置的值一样,所以徒劳增加判断次数,因此要哦再往前回溯。直到当前值与回溯值不相等,才有真正回溯的必要。

修改 next_find函数 的while 循环。 注意,上一段代码中,设置next[0] = -1,这里 要嵌套一个判断

		while ( j < T.length()-1){//注意 k的范围
			if(i==-1 ||  T.charAt(i) == T.charAt(j) ) {//当出现相同字符
				//next[j++]=i++;替换接下来三句。下次j+1位置配失败时(前提是j位置已经成功了),指针会退到 i+1 (因为i+1前的字符与j+1之前的k位一致)  处,
			
				i++;				
				j++;//继续往后遍历 看看重复子串长是多少
				if( T.charAt(i) != T.charAt(j) )
					next[j] = i;//原来解法只有这个没有 else 
				else {
				   if(next[i]== -1){  //如果这里 不做判断 那么后面好多个a 对应的 next 都随着 第一个字符 初始化 的 -1 来走,会导致数组溢出的错误
					   next[i]=0;
					}  
					next[j] = next[i];  //重点理解 本语句
				}
			}
			else{
				i = next[i];//遍历到字符不同的地方,前缀需要向前回溯了,回溯到
			}
		}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值