kmp_单模式匹配算法

文章目录


前言

最近学到了字符串模块,然后遇到了kmp,之前的哈希一直没看明白黑皮书上的代码,所以打算以后再说。然后就是看了两天的next数组求法,不明白当出现不等情况时为什么j=next[j],在掉了两天头发下,终于搞明白了。

一、KMP算法思想

1.思想

给你两个字符串,一个主串a,一个要匹配的串s
kmp_单模式匹配算法

相较于一个一个匹配,一旦遇到不相等字符时主串下一位与副串开头比较,kmp在比较时发现,遇到不同时,指向主串a的指针不需回溯,而是一直走到最后,相反,对于指向副串s的指针j回溯,
模拟:
首先正常比较,发现a!=b情况
kmp_单模式匹配算法
正常情况下我们可能会让i=1,j=0,再次遍历比较
kmp_单模式匹配算法
但我们不难发现对于s串,他有一个前后缀a
这里我们可以直接移动s而不改变i,减少比较回溯次数
kmp_单模式匹配算法
再如
遇到不匹配,但对于s有前后缀相等情况
kmp_单模式匹配算法
kmp_单模式匹配算法
kmp_单模式匹配算法
综上,我们每次只需记录副串s前后缀的位置,就可以知道我们j应该如何回溯了,这边就是next数组

2.next数组

从上面的模拟过程可以看出,next数组主要就是求副串s的最长相同前后缀,
下面是在哔站上找到的感觉最简单的代码,

kmp_单模式匹配算法
对于一个字符串s,我们找的是指向s的指针j之前的串中前后缀相等的个数,例如
kmp_单模式匹配算法
对于next[5] ( j从1开始),前缀ab==后缀ab,所以next[5]=2+1;
以下是粗略的求一个串的next数组的过程
kmp_单模式匹配算法
(具体文字思路在代码中)

3.kmp函数思路

我们只需每次遇到不匹配的时候,让j回溯到next[j]位即可,其余时候相等则i++;j++;当最终的指针j大小超过副串的长度的时候,说明匹配成功,返回匹配的开始位置即可

二、代码

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;


char a[1005];
char s[1005];

int getNext(char s[],int next[]) {
	int len=strlen(s);
//	int len=s.lengh();
	next[1]=0;
	int j=0;
	int i=1;
	while(i<len) {
		if(j==0||s[i]==s[j]) next[++i]=++j;//先i自增后再指向i位,i++; next[i]=++j
		// 而next[i++]是先指向i位,再对i自增  next[i]=++j; i++;
		else j=next[j];  //回溯到j位之前的前缀位置 然后在进行比较
		                 //为什么回溯到1-j前缀的位置
	                	 //因为对于1-i位前缀和后缀是一样的,而对于前缀的前缀(即1-j位的前缀)
		                 //同样也是后缀的前缀,
		                //前缀的前缀=前缀的后缀
		                //后缀=前缀
		                //后缀的前缀=后缀的后缀   =   前缀的前缀=前缀的后缀
		                //所以前缀(即j位前的前缀next[j])=i位的后缀,
		                //回溯到s[i]==s[j],此时,j前面的必定对于i前面的有相等的字符
		                //因为前i项找到了前缀,
		                //相当于一直缩小前缀的长度,找到符合的一项
	}

}

void kmp(char a[],char s[]) {
	int next[1005];

	int alen=strlen(a);
	int slen=strlen(s);
	getNext(s,next);
	int res=0;
	int i=0,j=0;

	while(i<alen&&j<slen){

		if(a[i]==s[j]){
			i++;
			j++;
		}
		else {
			j=next[j];
		}
	}
	if(j>=slen){
		cout<<i-slen<<endl;
	}
}
int main() {
	while(cin>>a>>s) {
		kmp(a,s);
	}
}

上一篇:线上500万数据查询时间在37秒,作者将问题解决了,我看到了更大的坑


下一篇:安装SQLyog-10.0.0-0