字符串匹配KMP算法实现

应用场景

  1. 已知有一个字符串str1 = "BBCABCDABABCDABCDBDE"和另外一个字符串str2 = "ABCDABD"
  2. 现在需要你来判断str1是否包含str2,如果包含返回一个true,不存在返回一个false

解决办法

  1. 暴力匹配法
  2. KMP算法匹配
暴力匹配法

在遇到这种匹配问题,我们一般想到的都是这种暴力匹配法,下面简单讲解一下他的实现方法。

暴力匹配法

  1. 创建两个变量i,j并且分别赋值为0,i为指向str1的索引,j为指向str2的索引
  2. 当我们str1.charAt(i) == str2.charAt(j)时即为匹配成功,并i++,j++,匹配下一个位置的字符
  3. 如果我们str1.charAt(i) != str2.charAt(j)时,i回溯到本次匹配第一个元素成功位置的后一位即i = i - j + 1,并将j赋值为0;
  4. 当我们有j的大小等于str2的长度时,即为匹配成功退出循环
暴力匹配代码实现
public static boolean violenceMatch(String str1, String str2) {
	int i = 0;
	int j = 0;

	while (i < str1.length() && j < str2.length()) {
		if (str1.charAt(i) == str2.charAt(j)) {
			i++;
			j++;
		} else {
			i = i - j + 1;
			j = 0;
		}
	}
	if (j == str2.length()) {
		return true;
	} else {
		return false;
	}
}
存在的问题

这种算法中存在大量的回溯,降低了效率,有些已经匹配过的字符也被匹配了。所以我们使用下面的KMP算法。

KMP算法
  1. KMP是一个解决模式串在文本串中是否出现过,如果出现过则找出最早出现的位置的经典算法。
  2. Knuth-Morris-Pratt字符串查找算法,简称为"KMP"算法
  3. KMP算法就是利用之前已经判断过信息,通过next数组,保存模式串中前后最长公共子序列的长度,每次回溯时,通过next数组找到,前面匹配过的位置,从而减少大量时间。
图解算法

在这里插入图片描述

  1. 第一步用str1的第一个字符去比较str2的第一个字符,发现不符合,将str2后移一位。
    在这里插入图片描述
  2. 发现还是不符合,再次后移,直至找到匹配位置
    在这里插入图片描述
  3. 按照之前的原则向下匹配,匹配符合,继续匹配str2第二个字符
    在这里插入图片描述
  4. 第二个字符也是符合,继续相同的步骤
    在这里插入图片描述
  5. 直至遇到一个不相匹配的字符,这个时候按照我们的暴力匹配方法,下一次回溯的位置应该是第三步B所在的位置,然而这种方式是非常不明智的,因为我们的BCD已经比较过了,没必要进行重复的工作。KMP的基本想法就是想办法把已经匹配过的字符去掉。
    在这里插入图片描述
  6. 如何才能将已经匹配过的值给他去除呢?这里需要我们计算出一个部分匹配表
    在这里插入图片描述
  7. 已知空格与D不相互匹配,但是前面的ABCDAB是匹配的,查表得知,最后一个匹配字符B对应的部分匹配表为2,因此按照下面公式得知向后移动的次数:移动位数 = 已匹配数 - 对应部分匹配值
    得知 4 = 6 - 2
    在这里插入图片描述
  8. 因为C与空格不匹配,这时部分匹配为"AB",得到他的部分匹配值为0,所以需要移动2位
    在这里插入图片描述
  9. 发现C与空格不相匹配,则向后移动一位在这里插入图片描述
  10. 一个字符一字符比较发现C与D不匹配,计算出需要移动4位
    在这里插入图片描述
  11. 最后发现全部匹配成功,程序结束
部分匹配表

首先讲解一下前缀与后缀
字符:bread
前缀: b, br, bre,brea
后缀: read, ead, ad, d

部分匹配值的产生就是找出前缀和后缀公共字符的长度

在这里插入图片描述

KMP算法代码实现
public boolean kmp(String str1, String str1, int[] next) {
	for (int i = 0;j = 0;i < str1.length();i++) {
		while (j > 0 && str1.charAt(i) != str2.charAt(j)) {
			j = next[j - 1];
		}
		if (str1.charAt(i) == str2.charAt(j)) {
			j++;
		}
	}
	if (j == str1.length()) {
		return true;
	} else {
		return false;
	}
}
public int[] getNext(String str) {
	
	int[] next = new int[str.length()];
	next[0] = 0;
	for(int i = 1,j = 0;i < str.length();i++) {
		while (j > 0 && str.charAt(i) != str.charAt(j)) {
			j = next[j - 1];
		}
		if (str.charAt(i) == str.charAt(j)) {
			j = j + 1;
		}
		next[i] = j;
	}
}

代码写完,技术有限,仅供参考,欢迎各位大神指点!!!!

发布了7 篇原创文章 · 获赞 3 · 访问量 271

猜你喜欢

转载自blog.csdn.net/justLym/article/details/104542397