KMP模式匹配算法

概念

串(string)的最重要的操作之一就是串的模式匹配。
说的这么高级,其实就是在一个主串里面找某一个子串的位置
而KMP是一种很快速的字符串查找方法。
既然提到KMP的高效性,就不得不拿出朴素模式匹配算法来进行对比一下子。

朴素的模式匹配算法

若是在长度为m的主串里找一个长度为n的子串
时间复杂度:O(m*n)
举个小栗子吧~
图片:

虽然这个栗子可能看起来并不是特别复杂,但是实际上,计算机处理的并不仅仅只是单单的字符,而是二进制0和1的串,一个ASCII码是一个8位的二进制01串,所以,比一个字符,计算机其实要计算八次,因此,使用朴素模式匹配算法,确实有些低效。

KMP模式匹配算法

这个算法是由三个科学家发明的,所以由他们的名字缩写命名为KMP

它的原理

通过对子串的预处理(作出next数组),有效减少字符串匹配时的回溯问题。
这个时候又上栗子啦~
在这里插入图片描述

对于一个主串S,一个子串P
主串S匹配到了i,子串P匹配到了j
这个时候j+1不再匹配,若是朴素算法,i会回到i+1,而此时如果j>i,这就称作回溯(因此进行了一些不必要的查找)。
而若是KMP算法,j+1匹配失败时,令i不变,j=next[j](next数组的建立马上解释鸭),这个操作是指,匹配失败时,子串P相对于文本串向右移动了j-next[j]位。
也就是,匹配失败时,子串向右移动的位数等于:匹配失败字符所在位置-匹配失败幅度的next值,即移动的实际位数为:j-next[j](这个值大于等于1)

next数组

首先放上next数组的公式,注意,next数组是针对子串而言的,在我看来,是对子串的预处理,而不是针对主串而言的
请添加图片描述

所以说,next数组各值的含义就是:
代表当前字符之前的字符串中,最大长度的相同前缀和后缀
如:abccccab的的最大相同前缀后缀就是ab,长度为2
(后缀无字符相等,当前next数组值为1,后缀有一个字符相等,当前next数组值为2,,依次推)
举个小栗子啦:
请添加图片描述

完整代码

输入主串和子串,输出子串的next数组,并输出子串下标

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
//注意此处引用的使用
//子串next数组创建函数
void kmp_next(string &s, vector<int> &next, int n)
{
	int k = -1;
	int j = 0;
	while (j < n)
	{
		if (k == -1 || s[j] == s[k])
		{
			next[++j] = ++k;
		}
		else
		{
			k = next[k];
		}
	}
}
//kmp算法实现函数
int KMP(string t, string p)
{
	int i = 0;							//主串位置
	int j = 0;							//模式串位置(一般都是默认从零开始)
	vector<int> next(p.size() + 1, -1); //为next数组赋初值,所有值都为-1
	kmp_next(p, next, p.size());		//创建next数组
	cout << "next数组如下:\n";
	for (int k = 0; k < next.size(); k++)
		cout << next[k] << " ";
	cout << endl;
	int tsize = t.size(); //这个地方size()需要转化,不然后面while循环会突然退出
	int psize = p.size();
	while (i < tsize && j < psize)
	{
		if (j == -1 || t[i] == p[j]) //若相等,则i和j右移,若是第一个元素,则默认是0
		{
			i++;
			j++;
		}
		else // i不需要回溯了 i = i - j + 1;
		{
			j = next[j];
		}
	}
	if (j == p.size())
		return i - j;
	else
		return -1;
}
int main(void)
{
	string T, S;
	cout << "请先后输入主串和子串\n";
	cin >> T >> S;
	cout << KMP(T, S);
	return 0;
}

运行结果

请先后输入主串和子串
abcdefgggg
fg
next数组如下:
-1 0 0
5

写在最后:
还是那句话哈哈哈,kmp其实也是个锦上添花,在一段大程序里降低一点时间复杂度的小算法。但是这个算法里面有很深的思想(小菜鸟表示很崇拜鸭),对于很对问题的解决还是有很大的借鉴之处的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KMP算法(Knuth-Morris-Pratt算法)是一种用于解决字符串匹配问题的高效算法。它的主要思想是利用匹配失败时的信息,尽量减少比较次数,提高匹配效率。 KMP算法的核心是构建一个部分匹配表(Partial Match Table),也称为Next数组。这个表记录了在匹配失败时应该将模式串向右移动的位置。 构建部分匹配表的过程如下: 1. 首先,将模式串中的第一个字符的Next值设为0,表示当匹配失败时,模式串不需要移动; 2. 然后,从模式串的第二个字符开始,依次计算Next值; 3. 当第i个字符与前面某个字符相同的时候,Next[i]的值为该字符之前(不包括该字符)的相同前缀和后缀的最大长度; 4. 如果不存在相同的前缀和后缀,则Next[i]的值为0。 有了部分匹配表之后,KMP算法的匹配过程如下: 1. 用i和j来分别表示模式串和主串的当前位置; 2. 如果模式串中的字符和主串中的字符相同,那么i和j都向右移动一位; 3. 如果模式串中的字符和主串中的字符不同,那么根据部分匹配表来确定模式串的下一个位置; 4. 假设当前模式串的位置为i,根据部分匹配表中的值Next[i],将模式串向右移动Next[i]个位置; 5. 重复上述步骤,直到找到匹配或者主串遍历完毕。 KMP算法的时间复杂度为O(m + n),其中m和n分别是模式串和主串的长度。相比于暴力匹配算法的时间复杂度为O(m * n),KMP算法能够大幅减少比较次数,提高匹配效率。 综上所述,KMP模式匹配算法通过构建部分匹配表并利用匹配失败时的信息,实现了高效的字符串匹配。在实际应用中,KMP算法被广泛地应用于文本编辑、数据搜索和字符串处理等领域。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值