首页 > 编程语言 >KMP字符串匹配算法——PMT数组的计算

KMP字符串匹配算法——PMT数组的计算

时间:2023-03-04 14:13:37浏览次数:51  
标签:PMT 匹配 前缀 后缀 needle ++ 算法 KMP

Leetcode 28.找出字符串中第一个匹配项的下标

KMP算法和PMT的介绍

  1. 如何更好地理解和掌握 KMP 算法? - 海纳的回答 - 知乎
  2. KMP算法PMT数组与next数组构造解释
KMP算法就是在暴力的基础上,记录之前已经匹配的部分,在失配时,减少重复比较的次数
  1. 前缀:不包含最后一个字符,包含第一个字符,连续子串
  2. 后缀:不包含第一个字符,包含最后一个字符,连续子串
  3. PMT数组:对应字符串的最大前缀与最大后缀相同时的长度

PMT数组的求法(模式匹配的过程)

字符串S与自己进行模式匹配

image

  1. i:主串的下标,j:模式串的下标

  1. j < i, i指的是当前字符串的后缀,j指的就是前缀

  1. PMT[i]:0 ~ i 的最大公共前缀后缀长度

  1. PMT[0] = 0 (当只有一个字符的时候,前缀和后缀都是空)

匹配规则(直接看具体过程会好理解)

存在的两个问题
  1. S[i] == S[j] ——> 说明:1 ~ i 与 0 ~ j 匹配成功,此时长度为:i 或 j + 1,选哪个?

  1. S[i] != S[j] ——> 说明:1 ~ i 与 0 ~ j 匹配失败,此时 j 应该往前移动,j 如何移动?
解决方法
  1. 在匹配成功之前,是可能出现失配的情况的,此时,匹配上的后缀长度,就不再是从 1 到 i 了,但前缀长度是固定的,都是 0 ~ j,所以当匹配成功时,PMT[i] = j + 1

  1. 首先 PMT[0...j-1] 都是计算完成的,也就是说失配时,0 ~ j-1 和 i-j ~ i-1 已经匹配成功,0 ~ j-1 的最长公共前缀后缀长度已经求出了,假设为 c,则 0 ~ c-1,和 j-c ~ j-1是相同的,所以此时 j 应该移动到 c ,即:j = PMT[j-1]
    • 0 ~ c-1 == j-c ~ j-1 == i-j ~ i-j+C-1 == i-c ~ i-1
    • 0 到 c-1,和 i-c 到 i-1 已经匹配成功,所以,下一次该匹配 S[c] 和 S[i]

  1. j 往前移动时,万一到 0 还未匹配上,此时就会超出有效下标,所以需要特判一下
具体过程
  1. i = 1,j = 0
    image
    S[i] == S[j] ——> 此时,1 ~ i == 0 ~ j,前缀后缀长度为1,所以 PMT[i] = 1
    i,j 同时后移

  1. i = 2,j = 1
    image
    S[i] != S[j] ——> 此时,1 ~ i != 0 ~ j, 前缀后缀收缩,前缀收缩为:2 ~ i,后缀收缩为:0 ~ j-1,S[i] 要与 S[j-1] 比较,...... 能不能利用PMT提高效率?

  1. i = 2,j = 0
    image
    S[i] != S[j] ——> 继续收缩,j 变为 -1,说明收缩到空,最长公共前缀后缀为空,PMT[2] = 0,i++

  1. i = 3,j = 0
    image
    S[i] == S[j] ——> PMT[i] = len(0 ~ j) = j + 1 = 1,i++,j++

  1. i = 4,j = 1
    image
    S[i] == S[j] ——> PMT[i] = j + 1 = 2,i++,j++

  1. i = 5,j = 2
    image
    S[i] != S[j] ——> 前缀,后缀开始收缩,但是 0 ~ j-1 与 i-j ~ i-1已经匹配成功,目前已知 0 ~ j-1 的最长公共前缀后缀 c,j 移动到最长公共前缀的后一个,也就是 c,即:j = PMT[j-1] = c = PMT[1] = 1

  1. i = 5,j = 1
    image
    S[i] != S[j] ——> 收缩,j = PMT[j-1] = PMT[0] = 0

  1. i = 5,j = 0
    image
    S[i] != S[j] ——> 收缩,此时前缀后缀为空,说明最长公共前缀后缀为空,即:PMT[i] = 0

总结与代码实现

总结

  1. PMT[0] = 0,i = 1,j = 0

  1. S[i] == S[j],PMT[i] = j+1,i++,j++

  1. S[i] != S[j], j > 0,j = PMT[j-1],返回步骤2

  1. S[i] != S[j],j == 0,PMT[i] = 0,i++

代码实现

GO实现
func GetPMT(needle string) []int {
	pmt := make([]int, len(needle))
	i := 1
	j := 0
	for i < len(needle) {
		if needle[i] == needle[j] {
			pmt[i] = j + 1
			j++
			i++
		} else {
			for needle[i] != needle[j] {
				if j > 0 {
					j = pmt[j-1]
				} else {
					pmt[i] = 0
					i++
					break
				}
			}
		}
	}
	return pmt
}

标签:PMT,匹配,前缀,后缀,needle,++,算法,KMP
From: https://www.cnblogs.com/01cainiao/p/17177923.html

相关文章

  • 算法基础大纲(持续更新)
    前言该文章是我跟着AcWing上买的算法基础课写的笔记。算法基础课的课程内容如下:第一章:基础算法1.1排序插入排序voidinsert_sort(){for(inti=1;i<n;......
  • 算法随想Day29【贪心算法】| LC122买卖股票的最佳时机Ⅱ、LC55-跳跃游戏、LC45-跳跃游
    LC122.买卖股票的最佳时机Ⅱ一旦遇到相比于昨天降价的,就抛出,就购入低价的,直到又遇到下一个滑坡点,又立即抛出,计算收益贪心算法表现在:总是在降价前抛出,获取收益,总是在降价......
  • 梯度提升算法决策过程的逐步可视化
    梯度提升算法是最常用的集成机器学习技术之一,该模型使用弱决策树序列来构建强学习器。这也是XGBoost和LightGBM模型的理论基础,所以在这篇文章中,我们将从头开始构建一个梯度......
  • (二)回溯算法: 组合数
    组合数问题描述给定两个整数n和k,返回范围[1,n]中所有可能的k个数的组合。感悟作为菜鸡的自己,这题一直是自己的心头之恨,上次和好友打比赛,遇到这题直接卡顿,想法......
  • (一)回溯算法
    概念:什么是回溯算法:回溯算法是对问题的一种穷举思想,及对于一些复杂的问题进行解析,一般采用递归,只是对一些穷举进行能优化(修枝),但是本质上还是穷举,原因是没有找到更好的方......
  • 大整数乘法-算法设计与分析
    分治法-大整数乘法输入:正负零不限,两数长度也不限。输入完第一个数后,回车,输入第二个数,回车。输出:两个数相乘的结果实现思路1.用C++实现大整数乘法2.算法性能优化对......
  • Bellman_ford和spfa算法
    Bellman_ford算法bellman_ford算法在要求起点到终点存在负权边,要求在指定k步(这是spfa无法替代的)bellman_ford和spfa都可以判断图中有无负权环......
  • 每日算法 230303
    每日算法230303题目1487.保证文件名唯一给你一个长度为n的字符串数组names。你将会在文件系统中创建n个文件夹:在第i分钟,新建名为names[i]的文件夹。由于两......
  • JVM系统优化实践(7):垃圾回收器与垃圾回收算法
    您好,我是湘王,这是我的51CTO博客,欢迎您来,欢迎您再来~上回说到了年轻代、老年代与数据计算的一个案例。接下来就先讲一讲年轻代和老年代的两个垃圾回收器:ParNew和CMS。和Serial......
  • 算法基础1.3.3高精度乘法
    前言先看高精度加法的文章,如果没有看,我把高精度加法文章中的总结前言放到这里该文章探讨的高精度代指C++中极大整数的计算,不是浮点数(y总说那个少见,不讲)。这个问题只在C......