首页 > 编程语言 >KMP字符串匹配算法

KMP字符串匹配算法

时间:2023-10-01 19:34:47浏览次数:35  
标签:匹配 后缀 ne len next ++ 算法 KMP 字符串

挑战最通俗的KMP算法讲解

什么是 \(KMP\)

KMP是一种用于模式串匹配问题的算法。
给出一个文本串和模式串,查询模式串在文本串中的(出现次数、出现位置等等)的问题称为“模式串匹配问题”。
KMP算法的本质是:针对模式串构建一个特定的数组,用于在匹配失败时减少后续匹配过程中的无用比较,可以将时间复杂度优化到线性

\(next\) 数组

设文本串为 \(s\),长度为 \(n\);模式串为 \(t\),长度为 \(m\)。
预处理一个 \(next\) 数组,对于 \(next[i]\),它表示在 \(t\) 的前 \(i\) 个字母中,最长公共前后缀的长度。
什么意思呢?我们举个栗子:
比如 \(t\) 是 \(ababaca\),则对应的 \(next\) 数组如下所示:

\(i\) \(1\) \(2\) \(3\) \(4\) \(5\) \(6\) \(7\)
\(next[i]\) \(0\) \(0\) \(1\) \(2\) \(3\) \(0\) \(1\)

\(next[1]\):前缀:{空};后缀:{空}。\(next\):\(0\)
\(next[2]\):前缀:{\(a\)};后缀:{\(b\)}。\(next\):\(0\)
\(next[3]\):前缀:{\(\color{red}a\), \(ab\)};后缀:{\(\color{red}a\), \(ba\)}。\(next\):\(1\)
\(next[4]\):前缀:{\(a\), \(\color{red}ab\), \(aba\)};后缀:{\(b\), \(\color{red}ab\), \(bab\)}。\(next\):\(2\)
\(next[5]\):前缀:{\(a\), \(ab\), \(\color{red}aba\), \(abab\)};后缀:{\(a\), \(ba\), \(\color{red}aba\), \(baba\)}。\(next\):\(3\)
\(next[6]\):前缀:{\(a\), \(ab\), \(aba\), \(abab\), \(ababa\)};后缀:{\(c\), \(ac\), \(bac\), \(abac\), \(babac\)}。\(next\):\(0\)
\(next[7]\):前缀:{\(\color{red}a\), \(ab\), \(aba\), \(abab\), \(ababac\)};后缀:{\(\color{red}a\), \(ca\), \(aca\), \(baca\), \(abaca\), \(babaca\)}。\(next\):\(1\)

如何进行匹配?

这里我们先假设我们已经求出了 \(next\) 数组(马上讲怎么求),我们来看看怎么进行匹配。
因为“next”这个词有可能被判断为关键字,所以接下来我们用“ne”来表示。
首先我们建立两个指针,分别为文本串的和模式串的。

int i = 0, j = 0;

然后,当文本串的指针还没有到达终点时,我们就接着匹配。

while (i < s.size()) {
    ...
}

如果当前的字母匹配,那就把两个指针都往后移一个字符。

if (s[i] == t[j]) i++, j++;

如果不匹配,那就把 \(j\) 挪动 \(next[j - 1]\) 格。

else if (j > 0) j = ne[j - 1];

如果还是不行,说明这是第一格就不匹配,把 \(i\) 往后挪。

else i++;

如果 \(j\) 到了终点,说明匹配成功。返回模式串匹配的起始坐标即可。

if (j == t.size()) return i - j;

\(next\) 数组的原理 & 如何求 \(next\) 数组?

刚才我们说了:如果不匹配,那就把 \(j\) 挪动 \(next[j - 1]\) 格。为什么可以这么做呢?
image

(这里用了一个b站大佬的图)
如上图,可以发现,我们成功匹配的画黄线的两个 \(AB\),和前面跳过的画蓝线的两个 \(AB\) 是完全一样的。所以我们可以直接跳过后两个 \(AB\) 接着匹配;这也就证实了 \(next\) 数组的本质:最长公共前后缀的长度(注意:最长公共前后缀不可以是子串本身,否则我们的移动就没有意义了)。

我们可以用递推的方式来求解 \(next\) 数组,比如我们现在已经知道当前的公共前后缀了,接下来分两种情况讨论:

  1. 下一个字符还是相同,直接构成了一个更长的公共前后缀;
  2. 下一个字符不相同了,那我们就再次察看左边的前缀,与右面的后缀再次进行检查下一个字符是否相同,就可以得到下一个字符的 \(next\) 数组了!

那么这样我们的代码实现就很简单了:

void buildnext(string t) {
	memset(ne, 0, sizeof ne);
	int len = 0; // 当前公共前后缀的长度
	int i = 1;
	while (i < t.size()) { // 依次生成每个next数值
		if (t[len] == t[i]) { // 下一个字符依然相同
			len++; // 长度+1
			ne[i] = len;
			i++;
		} else {
			if (len == 0) {
				ne[i] = 0; // 不存在直接为0
				i++;
			} else len = ne[len - 1]; // 是否存在更短的前后缀
		}
	}
}

最后放一下题和代码:

image

#include <bits/stdc++.h>
using namespace std;
int ne[100010];
void buildnext(string t) {
	memset(ne, 0, sizeof ne);
	int len = 0;
	int i = 1;
	while (i < t.size()) {
		if (t[len] == t[i]) {
			len++;
			ne[i] = len;
			i++;
		} else {
			if (len == 0) {
				ne[i] = 0;
				i++;
			} else len = ne[len - 1];
		}
	}
}
void kmp(string s, string t) {
	buildnext(t);
	int i = 0, j = 0;
	while (i < s.size()) {
		if (s[i] == t[j]) i++, j++;
		else if (j > 0) j = ne[j - 1];
		else i++;
		if (j == t.size()) cout << i - j << ' ';
	}
}
int main() {
	int n, m;
	string s, t;
	cin >> n >> t >> m >> s;
	kmp(s, t);
	return 0;
}

码字不易qwq
如果觉得这篇文章还不错的话,请点个赞吧!
如果有任何问题,欢迎在评论区提问,我会尽可能的第一时间回复!

标签:匹配,后缀,ne,len,next,++,算法,KMP,字符串
From: https://www.cnblogs.com/popfront/p/17737990.html

相关文章

  • C++常见算法&数据结构模版
    各种常见算法&数据结构模板1.最长不下降子序列(LIS)1.1\(O(n^2)\)做法点击查看代码for(inti=1;i<=n;i++){ cin>>a[i]; dp[i]=1; } for(inti=1;i<=n;i++){ for(intj=1;j<i;j++){ if(a[i]>a[j]){ dp[i]=max(dp[i],dp[j]+......
  • 视频融合/监控汇聚平台EasyCVR助力AI算法智能防溺水,实现水域监管
    防溺水已经成为青少年安全教育的重要内容,同时也是社会各界共同承担的安全管理责任。特别是在夏季,随着天气逐渐转热,溺水事故也进入了危险期、易发期和高发期。传统的预防和管理方法主要通过日常宣传演讲和人工巡逻来提醒人们溺水的危害,但存在一些问题:1)缺乏有效的安全预警设施:当人......
  • 线段裁剪:Cohen-Sutherland算法
    目录裁剪算法Cohen-Sutherland线段裁剪算法基本思想具体步骤计算分析程序代码裁剪算法计算机内部存储的图形数据量通常较大,而屏幕只显示其中一部分,因此需要确定哪些部分在显示区域内,哪些在显示区域外。这个过程称为裁剪(clipping)。裁剪是二维观察(三维观察)的重要部分,参见计算机图......
  • 全新注意力算法PagedAttention:LLM吞吐量提高2-4倍,模型越大效果越好
    前言 吞吐量上不去有可能是内存背锅!无需修改模型架构,减少内存浪费就能提高吞吐量!本文转载自新智元仅用于学术分享,若侵权请联系删除欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读、CV招聘信息。CV各大方向专栏与各个部署框架最全教程整理......
  • 基础算法:区间合并
    1、区间合并以AcWing.803为例,题目要求如下:给定n个区间[li,ri],要求合并所有有交集的区间。注意如果在端点处相交,也算有交集。输出合并完成后的区间个数。例如:[1,3]和[2,6]可以合并为一个区间[1,6]。输入格式第一行包含整数n。接下来n行,每行包含两个整数l和r。输......
  • 算法模板
    算法模板1.排序(1)快速排序(NoSTL)#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongintn,a[100010];voiddfs(intl,intr){ if(l>=r) return; intpos=(l+r)>>1; intx=a[pos]; inti=l,j=r; while(i<=j) {......
  • FFT变换算法
    FFT(FastFourierTransform)算法是一种高效的离散傅里叶变换(DFT)计算方法,它通过分解长度为N的DFT计算成若干个长度为N/2的DFT计算,从而大大减少了运算量。由于其快速、高效、稳定等特点,FFT算法在数字信号处理、图像处理、通信系统等领域得到了广泛应用。常用的FFT变换算法包括蝶形运算......
  • C/C++学习 -- 流加密算法(RC4算法)
    在信息安全领域,加密算法扮演着至关重要的角色。其中,RC4算法是一种广泛使用的流密码算法,用于数据的保密性和机密性。本文将深入探讨RC4算法的概述、特点、原理,以及提供C语言和C++语言实现RC4算法的代码案例。一、RC4算法概述RC4算法,又称RivestCipher4或Ron'sCode4,是一种流密码(St......
  • 雷德算法介绍
    雷德(Radix)算法是一种基于FFT(FastFourierTransform)算法的计算方法,其基本思想是将长度为N的DFT计算分解为O(logN)个长度为2的DFT计算,并通过不断的合并操作得到最终的结果。雷德算法的基本过程如下:将输入序列按二进制反转的顺序重新排列,以得到新的输入序列;将新的输入序列划分为两个......
  • 视频融合/视频汇聚平台加智能ai算法助力农业高质量生产
    我国是农业大国,随着新兴技术如AI的迅猛发展,大数据和互联网等技术已应用于农业生产中的各个环节,以提高土地利用率、降低成本、提高生产效率。智慧农业因此而兴起。智慧农业解决方案是根据农业生产的需求与现代网络发展状况而设计的。它利用人工智能技术,结合农业物联网、移动互联网......