【算法】KMP-快速文本匹配

news2024/11/17 5:32:58

文章目录

    • 一、KMP算法说明
    • 二、详细实现
      • 1. next数组定义
      • 2. 使用next加速匹配
      • 3. next数组如何快速生成
      • 4. 时间复杂度O(m+n)的证明
        • a) next生成的时间复杂度
        • b) 匹配过程时间复杂度
    • 三、例题
      • 1. [leetcode#572](https://leetcode.cn/problems/subtree-of-another-tree/description/)
      • 2. [leetcode#1367](https://leetcode.cn/problems/linked-list-in-binary-tree/description/)

一、KMP算法说明

要判断s1字符串是否包含s2字符串,如果包含返回s1中包含s2的最左开头位置,不包含返回-1

暴力方法就是s1的每个位置都做开头,然后去匹配s2整体,时间复杂度O(n*m),其中n为s1长度,m为s2长度

KMP算法可以做到时间复杂度O(n+m)

二、详细实现

1. next数组定义

字符串s的next数组为int数组,长度等于s的长度。next[i]表示在s中下标i之前子串的前缀和后缀的最大匹配长度(不包含整体)

以字符串"aabaabs"为例

// i=0,规定next[0]为-1
// i=1,由于s[1]之前只有a,除去整体,前缀和后缀只能是空,所以规定next[1]=0
// i=2, "aa",前缀"a",后缀"a",最大匹配长度1,next[2]=1
// i=3, "aab",没有可以匹配的前缀和后缀,next[3]=0
// i=4, "aaba", 前缀"a", 后缀"a", next[4]=1
// i=5, "aabaa", 前缀"aa", 后缀"aa", next[5]=2
// i=6, "aabaab", 前缀"aab", 后缀"aab", next[6]=3
// 扩充的next是可以多计算一位的
// i=7, "aabaabs",没有可以匹配的前缀和后缀,next[7]=0

2. 使用next加速匹配

func kmp(s1, s2 string) int {
	if len(s1) < len(s2) {
		return -1
	}
	next := nextArr(s2)
	x, y := 0, 0
	for x < len(s1) && y < len(s2) {
		if s1[x] == s2[y] {
			x++
			y++
		} else if y > 0 {
			y = next[y]
		} else {
			x++
		}
	}
	if y == len(s2) {
		return x - y
	} else {
		return -1
	}
}

3. next数组如何快速生成

func nextArr(s string) []int {
	if len(s) <= 1 {
		return []int{-1}
	}
	next := make([]int, len(s))
	next[0], next[1] = -1, 0
	cp := 0
	for i := 2; i < len(s); {
		if s[i-1] == s[cp] {
			cp++
			next[i] = cp
			i++
		} else if cp > 0 {
			cp = next[cp]
		} else {
			next[i] = 0
			i++
		}
	}
	return next
}

4. 时间复杂度O(m+n)的证明

a) next生成的时间复杂度
// for循环中我们关注i和i-cp
// i的范围是2~m
// i-cp的范围是0~m
// 分支1:i变大, i-cp不变
// 分支2:i-cp变大
// 分支3:i变大,i-cp变大
// 因此时间复杂度O(m)
b) 匹配过程时间复杂度
// for循环中关注x和x-y
// ...
// 同理时间复杂度O(n)

三、例题

1. leetcode#572

image-20240328005922263

// 思路:将两棵树都序列化为sRoot和sSubRoot,然后判断sSubRoot是否为sRoot的子串

func isSubtree(root *TreeNode, subRoot *TreeNode) bool {
	const nullVal = 1e4 + 1
	var s1, s2 []int
	s1 = encode(root, make([]int, 0), nullVal)
	s2 = encode(subRoot, make([]int, 0), nullVal)
	return kmp2(s1, s2) >= 0
}
func encode(root *TreeNode, list []int, nullVal int) []int {
	if root == nil {
		list = append(list, nullVal)
		return list
	}
	list = append(list, root.Val)
	list = encode(root.Left, list, nullVal)
	list = encode(root.Right, list, nullVal)
	return list
}
func kmp2(s1, s2 []int) int {
	if len(s1) < len(s2) {
		return -1
	}
	next := nextArrInt(s2)
	x, y := 0, 0
	for x < len(s1) && y < len(s2) {
		if s1[x] == s2[y] {
			x++
			y++
		} else if y > 0 {
			y = next[y]
		} else {
			x++
		}
	}
	if y == len(s2) {
		return x - y
	} else {
		return -1
	}
}
func nextArrInt(s []int) []int {
	if len(s) <= 1 {
		return []int{-1}
	}
	next := make([]int, len(s))
	next[0], next[1] = -1, 0
	cp := 0
	for i := 2; i < len(s); {
		if s[i-1] == s[cp] {
			cp++
			next[i] = cp
			i++
		} else if cp > 0 {
			cp = next[cp]
		} else {
			next[i] = 0
			i++
		}
	}
	return next
}

2. leetcode#1367

image-20240328010252351

func isSubPath(head *ListNode, root *TreeNode) bool {
	if head == nil {
		return true
	}
	if root == nil {
		return false
	}
	list := make([]int, 0)
	for head != nil {
		list = append(list, head.Val)
		head = head.Next
	}
	next := nextArrInt(list)
	return find(root, list, next, 0)
}

func find(cur *TreeNode, list []int, next []int, index int) bool {
	if index == len(list) {
		return true
	}
	if cur == nil {
		return false
	}
	for index >= 0 && cur.Val != list[index] {
		index = next[index]
	}
	// index=-1 => index=0
	// 匹配 => index+1
	index++
	return find(cur.Left, list, next, index) || find(cur.Right, list, next, index)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1550614.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python 从0开始 一步步基于Django创建项目(13)将数据关联到用户

在city_infos应用程序中&#xff0c;每个城市信息条目是关联到城市的&#xff0c;所以只需要将城市条目关联到用户即可。 将数据关联到用户&#xff0c;就是把‘顶层’数据关联到用户。 设计思路&#xff1a; 1、修改顶层数据模型&#xff0c;向其中添加‘用户’属性 2、根…

虹科Pico汽车示波器 | 免拆诊断案例 | 2018款东风风神AX7车发动机怠速抖动、加速无力

一、故障现象 一辆2018款东风风神AX7车&#xff0c;搭载10UF01发动机&#xff0c;累计行驶里程约为5.3万km。该车因发动机怠速抖动、加速无力及发动机故障灯异常点亮而进厂维修&#xff0c;维修人员用故障检测仪检测&#xff0c;提示气缸3失火&#xff1b;与其他气缸对调点火线…

HCIP-Datacom(H12-821)题库补充(3/28)

最新 HCIP-Datacom&#xff08;H12-821&#xff09;完整题库请扫描上方二维码访问&#xff0c;持续更新中。 如图所示组网&#xff0c;以下关于R4优选路由的描述&#xff0c;正确的是哪一项&#xff1f; A&#xff1a;无法判断&#xff0c;MED不能作为AS之间BGP选路的条件 B&a…

JAVA版鸿鹄云商B2B2C:解析多商家入驻直播带货商城系统的实现与应用

一、技术选型 java开发语言&#xff1a;java是一种跨平台的编程语言&#xff0c;适用于大型企业级应用开发。使用java开发直播商城可以保证系统的稳定性和可扩展性。 spring boot框架&#xff1a;spring boot是一个快速构建spring应用的框架&#xff0c;简化了开发过程&#xf…

2D Fantasy sprite bundle

使用精灵形状和手绘精灵创建独特的景观,非常适合侧滚动或平台游戏等2d游戏。 2D Fantasy sprite bundle是一个包含9个资源的捆绑包,价格折扣约为50%。 捆绑资产: -海岛精灵包, -森林精灵包, -地牢精灵包, -山精灵包, -古老的森林精灵包, -冰雪精灵包, -熔岩地牢精灵包…

MybatisPlus速成

MybatisPlus快速入门 快速入门入门案例常见注解常见配置 核心功能条件构造器自定义SQLService接口 扩展功能代码生成静态工具逻辑删除枚举处理器JSON处理器 插件功能分页插件通用分页实体 参考文档 mybatis-plus参考文档 全部资料链接 讲义 快速入门 入门案例 <dependency…

Phoenix伪分布安装

引言 Phoenix是构建在HBase上的一个SQL层&#xff0c;能让我们用标准的JDBC APIs而不是HBase客户端APIs来创建表&#xff0c;插入数据和对HBase数据进行查询。Phoenix完全使用Java编写&#xff0c;作为HBase内嵌的JDBC驱动。Phoenix查询引擎会将SQL查询转换为一个或多个HBase扫…

win11 查看 wifi 密码

** win11 查看 wifi 密码 ** 第一种方法&#xff1a; 1.点击桌面左下角的开始菜单&#xff0c;选择“设置”。 2.在网络和internet中选择“高级网络设置”。 3.在相关设置下方点击“更多网络适配器选项”。 4.右击“WLAN”&#xff0c;在WLAN信息窗口点击“状态”。 5.…

IoT是什么?如何实现设备智能化?

设备智能化 IoT 是什么&#xff1f; The Internet of Things,物联网&#xff0c;是指通过 各种信息传感器、射频识别技术、全球定位系统、红外感应器、激光扫描器等各种装置与技术&#xff0c;实时采集任何需要监控、 连接、互动的物体或过程&#xff0c;采集其声、光、热、电…

【单例模式】—— C++设计模式【附百度Apollo单例模式详细解读】

参考资料&#xff1a; &#xff08;1&#xff09;单例模式—— 代码随想录 &#xff08;2&#xff09;我给面试官讲解了单例模式后&#xff0c;他对我竖起了大拇指&#xff01; &#xff08;3&#xff09;C 单例模式详解 &#xff08;4&#xff09;单例模式之C实现&#xff0c;…

腾讯云2核2G服务器CVM S5和轻量应用服务器优惠价格

腾讯云2核2G服务器多少钱一年&#xff1f;轻量服务器61元一年&#xff0c;CVM 2核2G S5服务器313.2元15个月&#xff0c;腾讯云2核2G服务器优惠活动 txyfwq.com/go/txy 链接打开如下图&#xff1a; 腾讯云2核2G服务器价格 轻量61元一年&#xff1a;轻量2核2G3M、3M带宽、200GB月…

自然语言处理: 第二十章Dify本地部署

项目地址: dify a. 理论基础 Dify 是一个 LLM 应用开发平台&#xff0c;已经有超过 10 万个应用基于 Dify.AI 构建。它融合了 Backend as Service 和 LLMOps 的理念&#xff0c;涵盖了构建生成式 AI 原生应用所需的核心技术栈&#xff0c;包括一个内置 RAG 引擎。使用 Dify&a…

SpringBoot使用Jedis步骤

基础连接方式 引入依赖 <!-- Jedis --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency>创建Jedis对象&#xff0c;建立连接 操作字符串 方法名与Rdeis命令一致 操作Hash类型 释放资源 测…

数据结构/C++:位图 布隆过滤器

数据结构/C&#xff1a;位图 & 布隆过滤器 位图实现应用 布隆过滤器实现应用 哈希表通过映射关系&#xff0c;实现了O(1)的复杂度来查找数据。相比于其它数据结构&#xff0c;哈希在实践中是一个非常重要的思想&#xff0c;本博客将介绍哈希思想的两大应用&#xff0c;位图…

【力扣刷题日记】1113.报告的记录

前言 练习sql语句&#xff0c;所有题目来自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免费数据库练习题。 今日题目&#xff1a; 1113.报告的记录 表&#xff1a;Actions 列名类型user_idintpost_idintaction_datedateactionenumextravarc…

软件项目的外包开发流程

软件项目的开发流程是一个系统化的过程&#xff0c;旨在确保软件产品能够高效、稳定地满足用户需求。在中国&#xff0c;软件产业的发展得到了国家的大力支持&#xff0c;众多软件企业遵循国际标准和最佳实践&#xff0c;推动了软件工程的进步。 以下是一般的软件项目开发流程&…

盲盒小程序开发:探索未知,惊喜连连

在数字化浪潮的推动下&#xff0c;小程序以其便捷、轻量的特点&#xff0c;迅速成为移动互联网的新宠。而盲盒小程序的开发&#xff0c;则为用户带来了一种全新的、充满惊喜的购物体验。 盲盒小程序&#xff0c;顾名思义&#xff0c;就是结合了盲盒概念的微信小程序。它充分利…

Websocket的基本使用-聊天室案例

1. 什么是websocket WebSocket 是一种网络通信协议 2. 为什么需要websocket Websocket 协议&#xff0c;是为了弥补HTTP协议【单向通信】的缺点。 详细来讲&#xff0c;HTTP只支持由客户端发起通信&#xff0c;一旦服务端的数据发生变化&#xff0c;是不可能主动向客户端推…

数据结构——二叉搜索树详解

一、二叉搜索树定义 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 1.非空左子树上所有节点的值都小于根节点的值。 2.非空右子树上所有节点的值都大于根节点的值。 3.左右子树也都为二叉搜索树。 如下图所示&#xff1a…

SinoDB备份恢复工具之ontape

ontape是SinoDB数据库的备份恢复工具之一&#xff0c;可以记录、备份与恢复数据。因为不涉及到存储管理器&#xff0c;所以可以轻松实现其他备份方法&#xff0c;比如备份到 STDIO&#xff08;标准输出&#xff09;或备份到云。 1. ontape功能特性 备份数据库服务器实例支持增…