KMP 算法

KMP 算法介绍

  1. Knuth-Morris-Pratt 字符串查找算法,简称为“KMP算法”,常用于在一个文本串 S 内查找一个模式串 Р 的出现位置,这个算法由Donald Knuth、Vaughan Pratt、James H.Morris 三人于1977年联合发表,故取这3人的姓氏命名此算法。
  2. KMP 方法算法就利用之前判断过信息,通过一个 next 数组,保存模式串中前后最长公共子序列的长度,每次回溯时,通过 next 数组找到前面匹配过的位置,省去大量的计算时间
  3. 详细参考:https://www.cnblogs.com/zzuuoo666/p/9028287.html

KMP 算法最佳应用-字符串匹配问题

字符串匹配问题:

  1. 有一个字符串 str1=“BBC ABCDAB ABCDABCDABDE”,和一个子串 str2=“ABCDABD”
  2. 判断 str1 是否含有 str2,如果存在,就返回第一次出现的位置,如果没有,则返回 -1
  3. 要求:使用 KMP 算法完成判断,不能使用简单的暴力匹配算法.
思路分析图解
  1. 首先,用 str1 的第一个字符和 str2 的第一个字符去比较,不符合,关键词向后移动一位
    在这里插入图片描述
  2. 重复第一步,还是不符合,再后移
    在这里插入图片描述
  3. 一直重复,直到 str1 有一个字符与 str2 的第一个字符符合为止
    在这里插入图片描述
  4. 接着比较字符串和搜索词的下一个字符,还是符合。
    在这里插入图片描述
  5. 遇到 str1 有一个字符与 str2 对应的字符不符合
    在这里插入图片描述
  6. 这时候,想到的是继续遍历 str1 的下一个字符,重复第 1 步。(其实是很不明智的,因为此时 BCD 已经比较过了,没有必要再做重复的工作,一个基本事实是,当空格与 D 不匹配时,你其实知道前面六个字符是 ”ABCDAB”。KMP 算法的想法是,设法利用这个已知信息,不要把 ”搜索位置” 移回已经比较过的位置,继续把它向后移,这样就提高了效率。)
    在这里插入图片描述
  7. 可以对 str2 计算出一张《部分匹配表》,这张表的产生在后面介绍
    在这里插入图片描述
  8. 已知空格与 D 不匹配时,前面六个字符 ”ABCDAB” 是匹配的。查表可知,最后一个匹配字符 B 对应的 ”部分匹配值” 为 2,因此按照下面的公式算出向后移动的位数:
    移动位数 = 已匹配的字符数 – 对应的部分匹配值
    因为 6 - 2 等于 4,所以将搜索词向后移动 4 位。
  9. 因为空格与 C 不匹配,搜索词还要继续往后移。这时,已匹配的字符数为 2 (”AB”),对应的 ”部分匹配值” 为 0。所以,移动位数 = 2 - 0,结果为 2,于是将搜索词向后移 2 位。
    在这里插入图片描述
  10. 因为空格与 A 不匹配,继续后移一位
    在这里插入图片描述
  11. 逐位比较,直到发现 C 与 D 不匹配,于是,移动位数 = 6 - 2,继续将搜索词向后移动 4 位
    在这里插入图片描述
  12. 逐位比较,直到搜索词的最后一位,发现完全匹配,于是搜索完成。如果还要继续搜索(即找出全部匹配),移动位数 = 7 - 0,再将搜索词向后移动 7 位,这里就不再重复了。
    BBC ABCDABABCDABCDAEDFi !ABCDA电
  13. 介绍《部分匹配表》怎么产生的
    先介绍前缀,后缀是什么
    在这里插入图片描述
    “部分匹配值” 就是 ”前缀” 和 ”后缀” 的最长的共有元素的长度。
    以 ”ABCDABD” 为例,
    -”A” 的前缀和后缀都为空集,共有元素的长度为 0;
    -”AB” 的前缀为 [A];后缀为 [B],共有元素的长度为 0;
    -”ABC” 的前缀为 [A,AB],后缀为 [BC,C],共有元素的长度 0;
    -”ABCD” 的前缀为 [A,AB,ABC],后缀为 [BCD, CD, D],共有元素的长度为 0;
    -”ABCDA” 的前缀为 [A,AB,ABC,ABCD],后缀为 [BCDA,CDA,DA,A],共有元素为 ”A”,长度为 1;
    -”ABCDAB” 的前缀为 [A,AB,ABC, ABCD,ABCDA],后缀为 [BCDAB,CDAB,DAB,AB,B],共有元素为 ”AB",长度为 2;
    -”ABCDABD”的前缀为 [A,AB,ABC,ABCD,ABCDA,ABCDAB],后缀为 [BCDABD,CDABD, DABD,ABD, BD],共有元素的长度为 0;
  14. ”部分匹配” 的实质是,有时候,字符串头部和尾部会有重复。比如,”ABCDAB” 之中有两个 ”AB”,那么它的 ”部分匹配值” 就是 2 (”AB” 的长度)。搜索词移动的时候,第一个 ”AB” 向后移动 4 位(字符串长度 - 部分匹配值),就可以来到第二个 ”AB” 的位置。
    在这里插入图片描述
代码实现
public class KMPAlgorithm {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		String str1 = "BBC ABCDAB ABCDABCDABDE";
		String str2 = "ABCDABD";
		
		int[] next = kmpNext("ABCDABD"); 
		System.out.println("next=" + Arrays.toString(next));
		
		int index = kmpSearch(str1, str2, next);
		System.out.println("index=" + index); 
		
		
	}
	
	//写出kmp搜索算法
	/**
	 * 
	 * @param str1 源字符串
	 * @param str2 子串
	 * @param next 部分匹配表, 是子串对应的部分匹配表
	 * @return 如果是-1就是没有匹配到,否则返回第一个匹配的位置
	 */
	public static int kmpSearch(String str1, String str2, int[] next) {
		
		//遍历 
		for(int i = 0, j = 0; i < str1.length(); i++) {
			
			//需要处理 str1.charAt(i) != str2.charAt(j), 去调整j的大小
			//KMP算法核心点, 可以验证...
			while( j > 0 && str1.charAt(i) != str2.charAt(j)) {
				j = next[j-1]; 
			}
			
			if(str1.charAt(i) == str2.charAt(j)) {
				j++;
			}			
			if(j == str2.length()) {//找到了 // j = 3 i 
				return i - j + 1;
			}
		}
		return  -1;
	}

	//获取到一个字符串(子串) 的部分匹配值表
	public static  int[] kmpNext(String dest) {
		//创建一个next 数组保存部分匹配值
		int[] next = new int[dest.length()];
		next[0] = 0; //如果字符串是长度为1 部分匹配值就是0
		for(int i = 1, j = 0; i < dest.length(); i++) {
			//当dest.charAt(i) != dest.charAt(j) ,需要从next[j-1]获取新的j
			//直到发现有 dest.charAt(i) == dest.charAt(j)成立才退出
			//这时kmp算法的核心点
			while(j > 0 && dest.charAt(i) != dest.charAt(j)) {
				j = next[j-1];
			}
			
			//当dest.charAt(i) == dest.charAt(j) 满足时,部分匹配值就是+1
			if(dest.charAt(i) == dest.charAt(j)) {
				j++;
			}
			next[i] = j;
		}
		return next;
	}
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玳宸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值