左神算法笔记(十二)——KMP算法

KMP算法详解与应用

解决原始问题:str1和str2为两个字符串,其中str1中的某个子串是否等于str2.
明确定义子序列可以不连续,子串/子数组一定是连续的。(中间可能会出现混用情况,必须要区分清楚)
getIndexOf(str1,str2)就是str2是否包含在str1中,包含返回true,不包含返回false。中间其实就采用了KMP算法。
暴力算法:将str1从最开始的位置开始配,如果第一个跟str2中的首位符合,则接着向下比,如果不符合则将str2首项和str1首项开始比较。
复杂度O(n*m).

KMP流程介绍:

  1. 在一个字符串中,利用一个字符之前的字符串最长前缀和最长后缀的匹配长度进行简化求解。
    (前缀和后缀均不能取字符的前面所有长度。len<qian.length-1)
  2. 先求出str2的信息:生成整形next数组,返回每个位置上的最长前缀和最长后缀的匹配长度。例如ababac这个str2中,形成的next数组为[-1 0 0 1 2 3]。具体如何在下面
  3. 假设str1中i位置开始配,一路都相等,到了其中某一个位置不等。由于关于str2求出了一个信息,所以next可以告知不等位置之前的匹配长度。
  4. 利用匹配长度,则str2中的某个位置和str2中的起始位置对应,然后顺次找到str1中的j位置,此时str2从j位置开始匹配,并且匹配长度的存在,所以从上个位置不等的位置开始匹配。
    课程相关内容

这个算法流程将i位置到j位置中间的位置确保配不出str2,此时中间的这些位置均不再进行判定,节省时间。

public static int getIndexOf(String s,String m){
	if(s == null || m == null ||m.length()<1 ||s.length<m.length){
		return -1;
	}
	//下面就是KMP算法的全部步骤
	char[] str1 = s.toCharArray();
	char[] str2 = m.toCharArray();
	int i1 = 0;
	int i2 = 0;
	//求匹配的数组
	int[] next = getNextArray(str2);
	while(i1<str1.length && i2<str2.length){
		if(str1[i1] == str[i2]){
			i1++;
			i2++;
		//-1为起始位置
		}else if(next[i2] == -1){
			i1++;
			//此时i1已经到了不等的位置,此时直接让i2直接划过匹配的部分,从不等的位置开始
		}else{
			i2 = next[i2];
		}
	}
	//如果i2滑到最后了,那证明找到了,如果到最后都不等于,那返回负一,没找到
	return i2 = str2.length ? i1-i2:-1;

匹配长度求解流程:

  1. 首先确定好第i个位置上的匹配长度L
  2. 第i+1位置匹配长度判断时,需要判断第i位置上匹配长度中前半段A的下个字符和第i位置上的字符进行比较
  3. 如果相等则第i+1位置匹配长度为L+1
  4. 如果不相等将前半段A的匹配长度读出,再分出A的前半段B,再返回步骤2,进行判断。
  5. 最后前半段中只包含整个字符串中的第一个字符,此时还不相等则返回0,相等则返回1。
public static int[] getNextArray(char[] str2){
	if(str2.length == 1){
		return new int[] {-1};
	}
	int[] next = new int[str2.length];
	next[0] = -1;
	next[1] = 0;
	//i代表数组开始的位置
	int i = 2;
	//cn代表匹配长度
	int cn = 0;
	while(i<next.length){
		//如果相等,则下一个字符的匹配长度加一
		if(str2[i-1] == str2[cn]){
			next[i++] = ++cn;
		//如果不相等,则此时使得匹配长度变成cn处的匹配长度值,然后重复整个while循环,再进行判断,此时i的值不会改变,会一直进行判断,直到最后cn=0的时候让匹配长度为0,进行下一个判断。
		}else if(cn>0){
			cn = next[cn];
		}else{
			next[i++] = 0;
		}
	}
	return next;
}

相关题目

1.给出一个数组,问最少需要添加什么字符串使得整个字符串包含两个原始字符串。

这道题其实就是相当于考察匹配长度的计算过程,首先计算好整个原始字符串的匹配长度数组,然后根据最后一个字符的匹配长度可以知道跟起始位置的匹配长度,从而原始字符串匹配字符串后面的字符串就是需要添加的内容,并且可以保证添加的内容最少。

2.如何判断一个字符串不是由一个子串重复多次得到的

如果是由子串重复多次得到的,则在终止条件下,每个匹配长度都是最开始的匹配长度的相应增加数目,同时数组长度是子串长度的倍数,和匹配数组会存在关系。这个题目没有讲解,可以思考一下。

发布了24 篇原创文章 · 获赞 6 · 访问量 494

猜你喜欢

转载自blog.csdn.net/qq_35065720/article/details/104201229