KMP字符串模式匹配算法

KMP算法解决什么问题呢?

给定一个模式串 S,以及一个模板串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。

模板串 P 在模式串 S 中多次作为子串出现。

求出模板串 P 在模式串 S 中所有出现的位置的起始下标。

或者是,寻找S串中是不是存在P

我们都能很轻松的想到,如果我们要匹配,我们可以把P串的第一位对应S串的每一个字符来进行匹配,如果匹配成功了就匹配P的第二个字符和当前匹配好的S的下一个来再进行匹配;如果中途不能匹配了,就重新从P的头匹配刚才S进行匹配的下一个字符(可能描述不准确,就是暴力匹配

那么相当于我们每次都把P进行向后移动一位。

那么我们实际上是可以知道,它最多往后到什么时候能继续进行匹配?


假设现在是不能匹配的时候,到了黄色圈那个字符不能匹配了
在这里插入图片描述

那我这个时候就应该把红色的模版串往后移使得他们能够继续匹配

假设移动到了这里

在这里插入图片描述

这时候能匹配上了(当然可能不会有重复的),然后我们能观察发现

在这里插入图片描述

这个时候黄色标注的区域是相等的

绿色的又是P前面的那些

在这里插入图片描述

那么这相当于什么,相当于我们在该串中,他的以右边黄色为结尾的该子串的前缀和后缀是相等的(注意这里后缀只是结尾是一定的,比较的时候也得从左往右比较相等)

那么,前缀和后缀相等的最大长度,就是我们从前往后移动P的最短距离

我们设next数组表示, 模式串中每个前缀最长的能匹配前缀子串的结尾字符的下标

next[i] = j 表示下标以i - j + 1为起点,i为终点的后缀和下标以1为起点,j为终点的前缀相等,且此字符串的长度最长

即p[1,j] = p[i - j + 1, i]

那么我们前面的那些都匹配过了,就肯定知道前面与S串的部分是相同的,而往后移动的距离就是 j j j

因为我们要让他们匹配相等

在这里插入图片描述

而这里对next的含义解释实际上就是我们匹配到这里下一个从这里匹配就行

匹配的过程就是:

// kmp匹配过程
for(int i = 1, j = 0; i <= m; i++){ // 枚举当前的s[i],和匹配的是p[j+1]
    while(j && s[i] != p[j + 1]) // j没有退回起点
        j = ne[j]; // 如果还不能匹配,那么我看我新的这个点的下一个点能不能和s[i]匹配
    	// 为什么是下一个点呢,因为我们根据next数组的定义就知道,当前j肯定是和前面相等的(不知道我这里解释的对不对,还请看到的大佬指正)
    if(s[i] == p[j + 1]) j++; // 如果匹配了就可以匹配下一个位置
    if(j == n){
        // 成功
    }
}

求next过程,我们根据定义就可以知道,实际上求next并不需要S串

如何求next呢?

我们匹配i-1和j,如果不相等,我们退而求其次,即再匹配ne[j]和i是不是相等的,如果不行就再退

在这里插入图片描述

// next求解
// next[1] = 0,如果第一个字母失败了,只能从0开始
for(int i = 2, j = 0; i <= n; i++){
    while(j && p[i] != p[j + 1]) j = ne[j];
    if(p[i] == p[j + 1]) j++;
    ne[i] = j;
}
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1e5 + 10, M = 1e6 + 10;

int n, m;
char p[N];
char s[M];
int ne[N];

int main(){
	cin >> n;
	scanf("%s", p + 1);
	cin >> m;
	scanf("%s", s + 1);
	
	for(int i = 2, j = 0; i <= n; i++){
		while(j && p[i] != p[j + 1]) j = ne[j];
		if(p[i] == p[j + 1]) j++;
		ne[i] = j;
	}
	
	for(int i = 1, j = 0; i <= m; i++){
		while(j && s[i] != p[j + 1]) j = ne[j];
		if(s[i] == p[j + 1]) j++;
		if(j == n){
			cout << i - n + 1<< " "; // 全部匹配完了,而且全部匹配上了,用当前的点减去模版串的长度就是起点了
			j = ne[j]; // 匹配成功再往后退一步来看剩下的j的后缀还有没有与P相同的
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 简单字符串模式匹配算法:也称为朴素字符串匹配算法,是一种基础的字符串匹配算法。它的思想是从主的第一个字符开始,依次比较主和模式中对应位置的字符是否相等,如果相等则继续比较,直到模式中所有字符匹配成功,或者有一个字符匹配为止。如果不匹配,则将主的起始位置向后移动一位,重新开始匹配。该算法的时间复杂度为O(m*n),其中m和n分别为主和模式长度。 2. 首位字符串模式匹配算法:也称为BF算法(Brute Force),是一种改进的字符串匹配算法。它的思想是在简单字符串模式匹配算法的基础上,当发现主中某个字符与模式中的某个字符匹配时,不是将主的起始位置向后移动一位,而是将模式的起始位置向前移动到上一次比较成功的位置之后的下一位,继续匹配。这样可以减少比较次数,提高匹配效率。该算法的时间复杂度为O(m*n),其中m和n分别为主和模式长度。 3. KMP字符串模式匹配算法:是一种高效的字符串匹配算法。它的核心思想是利用模式自身的特性,预处理出一个next数组,使得在匹配过程中,当出现不匹配的情况时,可以通过next数组中的信息,跳过一部分比较,从而提高匹配效率。具体实现方法是,在预处理next数组时,从模式的开头开始,计算出每个位置对应的最长前缀和最长后缀的公共部分长度,保存在next数组中。在匹配过程中,当出现不匹配的情况时,根据next数组中的信息,将模式的起始位置向后移动一定的距离,从而跳过一些比较。该算法的时间复杂度为O(m+n),其中m和n分别为主和模式长度

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值