一文吃透KMP算法

2023-11-19

前提 :

假设我们在字符串 “bacbababaabababca”中 搜寻字符串 “abababca”是否存在。

KMP算法过程

下面就KMP算法的匹配过程进行阐述。

step0 :在执行匹配之前,先定义几个概念:“前缀集合”,”后缀集合”,”部分匹配值”
“前缀集合”指除了最后一个字符外,一个字符串的全部头部组合; “后缀集合”指除了第一个字符外,一个字符串的全部尾部组合。 “部分匹配值”指”前缀集合”与”后缀集合”的最长公共元素的长度

举个例子,如字符串 “a”,前缀集合用prefixes表示 (下同),按照定义除了最后一个字符,则为空,所以prefixes={},后缀集合用suffixes表示(下同),按照定义除了第一个字符也为空,即suffixes={},所以集合 prefixes与 suffixes交集也为空,两个集合的公共元素的长度(即部分匹配值)为0,我们称”a”的部分匹配值为0

再比如字符串 “abab”,则prefixes = {“a”,”ab”,”aba”},suffixes={“b”,”ab”,”bab”},则前缀集合与后缀集合的交集:{ ”ab“} ,长度为2,即”abab”的部分匹配值为2

3.有些字符串比较特别如 “ababa”,其prefixes={“a”,”ab”,”aba”,”abab”},suffixes={“a”,”ba”,”aba”,”baba”},这时候prefixes与suffixes的交集就变成了 {“a”,”aba”},公共元素有两个,这个时候部分匹配值我们取最大的元素”aba”,长度为3

这样我们就能得到字符串“abababca”的部分匹配表

  char:  | a | b | a | b | a | b | c | a |
  index: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
  value: | 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 |

下面开始KMP算法的字符串匹配过程

step1-首先从第一位进行字符串比较:
bacbababaabababca
abababca

可以看到b 与a 并不相等,此时我们直接后移一位

step2-第三个字符“c”时候不匹配
bacbababaabababca
abababca

这个时候已经匹配了字符“a”,按照部分匹配表,其部分匹配值为0,
这个时候后移动位数= 已经匹配的字符数-已经匹配的字符对应的部分匹配值 = 1 - 0 = 1

step3-而移动后的:
bacbababaabababca
abababca

c与a 又不相等,继续移动 直到

step4-

bacbababaabababca
  abababca

这次的匹配直到a 与b字符不相等,这个时候已经匹配的字符为“ababa”,长度为5,“ababa”对应的部分匹配值通过查表为3

后移位数= 已经匹配的字符数-已经匹配字符对应的部分匹配值 = 5-3 = 2

step5-移动得到

bacbababaabababca
    abababca

这个时候 后移位数 = 3-1 =2

step6- 再次移动后

bacbababaabababca
     abababca

只匹配了a之后又不匹配,则按照匹配表又往后移动一位

step7- 这个时候:

bacbababaabababca
     abababca 发现字符串全部匹配,至此匹配结束

总结

如果字符串发生了匹配,那么根据部分匹配表进行的后移之后一定会让字符串的首字母再匹配(有可能会从首字母开始匹配多个),后移位数完全取决于查找的字符串,跟需要被查找的字符串没有关联。

下面附上 KMP算法的java代码实行 Download here

package com.util;

import org.apache.log4j.Logger;

/**

  • @author ycx

*/
public class KMPStringMatcher {
private static final Logger logger = Logger
.getLogger(KMPStringMatcher.class);
private char[] targetArr = null;
private char[] sourceArr = null;
private int[][] partionMatchTable = null;
private static final int NOT_MATCH = -1;

public static void main(String[] args) {
	String  target = "BBC ABCDAB ABCABCDABDDABCDABDE";
	String  match = "ABCDABD";
	
	KMPStringMatcher kmp = new KMPStringMatcher(target);
	System.out.println(kmp.find(match));
	
	
}

/**
 * 构造器
 * 
 * @param targetStr
 */
public KMPStringMatcher(String targetStr) {
	if (targetStr == null)
		throw new NullPointerException("target string can not be null");
	this.targetArr = targetStr.toCharArray();
}

/**KMP算法匹配字符串的入口函数
 * @param match
 * @return
 */
public synchronized int find(String match) {
	if (match == null)
		throw new NullPointerException("match string can not be null");

	char[] matchArr = match.toCharArray();
	if (sourceArr == null || !isTwoCharArrEqual(sourceArr, matchArr)) {
		calculatePartionMatchTable(matchArr);
		sourceArr = matchArr;
	}

	if (targetArr.length < sourceArr.length)
		return NOT_MATCH;

	return getFirstPartionMatched(matchArr);

}

/**返回完全匹配的第一个字符位置
 * @param matchArr
 * @return
 */
private int getFirstPartionMatched(char[] matchArr) {
	int i = 0, j = 0, moveStep = 0;
	int firstPartion = NOT_MATCH;
	while (j < matchArr.length && i < targetArr.length) {
		if (matchArr[j++] != targetArr[i++]) {
			moveStep = (j == 1) ? 1 : (j - 1 - partionMatchTable[1][j - 2]); //后移位数
			i = i -j + moveStep;
			j = 0;
			continue;
		}

		if (j == matchArr.length) {
			firstPartion = i - j;
			break;
		}
	}

	return firstPartion;

}

/**计算部分匹配表:算法的关键
 * 
 * @param matcherString
 * @return
 */
private boolean calculatePartionMatchTable(char[] match) {
	partionMatchTable = new int[2][match.length];
	partionMatchTable[0][0] = 0;
	partionMatchTable[0][1] = 0;

	int length = 0;
	char[] a, b;
	for (int i = 1; i < match.length; i++) {
		length = 0;
		for (int j = 0; j < i; j++) {
			a = getSubArr(match, 0, j);
			b = getSubArr(match, i - j, i);
			if (isTwoCharArrEqual(a, b))
				length = (a.length > length) ? a.length : length;
		}

		partionMatchTable[0][i] = i;
		partionMatchTable[1][i] = length;
	}

	return true;
}

/** 从一个字符数组中提取从 start 开始到 end 结束的子数组
 * @param target
 * @param start
 * @param end
 * @return
 */
public static char[] getSubArr(char[] target, int start, int end) {
	if (target == null || start < 0 || end >= target.length) {
		return null;
	}

	if (target == null || target.length == 0)
		throw new NullPointerException();

	char[] charArr = new char[end - start + 1];
	int count = start;
	int targetCount = start;
	while (count <= end)
		charArr[(count++) - start] = target[targetCount++];

	return charArr;

}

/** 判断两个字符数组是否equal :基于数组元素判断
 * @param from
 * @param to
 * @return
 */
public static boolean isTwoCharArrEqual(char[] from ,char[] to){
	if(from == null || to == null)
		return false ;
	
	if(from.length != to.length)
		return false ;
	
	int i = 0;
	int j = 0;
	int count =0;
	while(from[i++] == to[j++]){
		++count ;
		if(i >= from.length)
			break;
	}
	
	
	return (count == from.length)?true:false;

}

/**替换被匹配的目标文本
 * @param replaceStr
 * @return
 */
public synchronized boolean  replaceTheTargetStr(String replaceStr){
	if(replaceStr == null || replaceStr.equals(""))
		throw new NullPointerException();
	
	this.targetArr = replaceStr.toCharArray();
	
	return true ;
	
}

}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一文吃透KMP算法 的相关文章

随机推荐

  • 变度量法算法(DFP)求解无约束问题

    程序功能 1 变度量法算法 DFP 求解无约束问题 2 调用文件夹下Newton的子函数 nfx ndfx ndfx2 vectorLength 3 z3 A i b 计算当前d的值 矩阵计算可能存在奇异值 4 请根据不同的目标函数 设置精
  • android- Cause: Unknown command-line option '-X'.

    问题太简单了 直接解决办法 File gt Settings gt Build Execution Deployment gt Compiler 删除Command line options里面的内容 重新gradle 感谢博主 欢迎留言指
  • 全栈之前端

    欢迎关注 全栈工程师修炼指南 公众号 点击 下方卡片 即可关注我哟 设为 星标 每天带你 基础入门 到 进阶实践 再到 放弃学习 花开堪折直须折 莫待无花空折枝 作者主页 https www weiyigeek top 博客 https b
  • 2022面试题汇总

    目录 浏览器下两个页面的通讯都有什么方式 使用css与js做一个九宫格动画 请输出如下的代码打印结果 js如何实现页面地址发生变化 但页面不发生跳转 请用js实现 请用多种方式实现垂直居中 实现的方式越多越好 请实现一个getValue函数
  • 【深度学习】全面直观认识深度神经网络

    01深度学习的精准定义 一类通过多层非线性变换对高复杂性数据建模算法的集合 它的两个非常重要的特征是多层性和非线性 俗称多层非线性变换 所以深度学习要去线性化 为什么呢 因为线性模型存在局限性 任意线性模型得到组合仍然还是线性模型 所以只要
  • Linux如何找回或者重置root用户密码

    欢迎参与个人独立开发的阅时即查web APP公测 请扫码体验 第一个为旧版 第二个为2019年6月版 在Linux这样一个权限管理严格 系统安全性要求高的环境中 根用户 超级用户 root的的密码显得十分重要 但是还是有一些马大哈会忘记自己
  • 【LLM】深入剖析 GOOGLE PALM 2:全面概述

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • 用一维字符数组存放字符串

    一 用一维字符数组存放字符串 1 C语言对字符串的约定 字符串是借助于字符型一维数组来存放的 并规定以字符 0 作为字符串的结束标志 0 作为标志占用存储空间 但不计入串的实际常量 2 C语言中表示字符串常量的约定 虽然c语言中没有字符串数
  • regex_replace()函数的应用与解析

    include
  • lua报错 module 'Module' not found

    这几天学习lua使用require关键字获取自己定义的模块式 发现报没有这个模块文件 询问老师 老师说是因为中文路径问题 的确这个可能会出现问题 但是我修改后还是报这个错误 老师就让我看他的源代码 我确定没写错 所以还是要靠自己来解决了 终
  • 【sql语句基础】——查(select)(合并查询)

    目录 合并查询 单独查询 合并查询 UNION ALL UNION ALL定义 UNION ALL代码示例 UNION ALL查询结果 合并查询 UNION ALL UNION 定义 UNION 代码示例 UNION 查询结果 合并查询 当
  • Android Button 背景高度被拉伸问题

  • Linux音频之ASOC

    参考 https blog csdn net droidphone article details 7165482 1 ASOC简介 ASoC ALSA System on Chip 是建立在标准ALSA驱动层上 为了更好地支持嵌入式处理器
  • 第八章、Linux 磁盘与文件系统管理

    系统管理员很重要的任务之一就是管理好自己的磁盘文件系统 每个分割槽不可太大也不能太小 太大会造成磁盘容量的浪费 太小则会产生文件无法储存的困扰 此外 我们在前面几章谈到的文件权限与属性中 这些权限与属性分别记录在文件系统的哪个区块内 这就得
  • 贝叶斯网络学习

    状态空间搜索 如果按专业点的说法就是将问题求解过程表现为从初始状态到目标状态寻找这个路径的过程 通俗点说 两点之间求一线路 这两点是求解的开始和问题的结果 而这一线路不一定是直线 可以是曲折的 由于求解问题的过程中分枝有很多 主要是求解过程
  • 神经网络——实现MNIST数据集的手写数字识别

    由于官网下载手写数字的数据集较慢 因此提供便捷下载地址如下 手写数字的数据集MNIST下载 https download csdn net download gaoyu1253401563 10891997 数据集包含如下 一 使用小规模数
  • 超级简单!vue解决前后端跨域问题,看完就会

    在Vue中解决前后端跨域问题 需要通过配置和设置代理来实现 配置 在Vue的config目录下的index js文件中 找到devServer选项 在其中添加如下代码 devServer proxy api target http loca
  • mysql my-innodb-heavy-4g.cnf_my-innodb-heavy-4G.cnf 配置文件

    client 客户端配置 port 3306 mysql连接时默认的端口号 socket tmp mysql sock 用于连接mysql mysqld 服务端配置 port 3306 mysql服务默认监听的端口 socket tmp m
  • window opengl

    接口 https www khronos org registry OpenGL api GL
  • 一文吃透KMP算法

    前提 假设我们在字符串 bacbababaabababca 中 搜寻字符串 abababca 是否存在 KMP算法过程 下面就KMP算法的匹配过程进行阐述 step0 在执行匹配之前 先定义几个概念 前缀集合 后缀集合 部分匹配值 前缀集合