KMP——字符串匹配

在保守代码虐待之后,舒缓心情的最好方式就是记录一篇博客
(不知为何难以缩进) ——阿寇

A brief/simple introduction of KMP

KMP算法是一个实现字符串匹配的高级算法。

  • 普通算法的实现通常比较简单,比如串ababad匹配abad。假设我们把第一个串称作string,第二个串称作pattern。常规做法:a-a;b-b;a-a;b不能匹配d,此时从string的第二个字符开始重新和pattern匹配,发现b不能和a匹配;再从string的第三个字符依次和pattern匹配,最终成功。
  • 很显然是一个 双循环,复杂度为O(strlen(string)*strlen(pattern));
  • 而KMP算法则采用了非常大的脑洞,通过寻找pattern子串最长前后缀来进行高效的字符串匹配操作。

前后缀是什么?

  • For example,拿abad这个pattern来说,a是ab的前缀,b是ab的后缀;a和ab是aba的前缀,a和ba是aba的后缀;a、ab、aba是abad的前缀,d、ad、bad是abad的后缀。

计算next/match/failure数组

  • 同样,以abad为例。next数组是为pattern求的,所以pattern的长度等于next数组的长度。
  • next[0]默认为-1;由于ab的前缀和后缀没有匹配的,所以next[1]=-1;由于aba的前缀之一a和后缀之一a匹配,因此next[2]=前缀a出现的下标=0;而abcd的前后缀无一匹配,因此next[3]=-1;
    我尽量举例解释,根据例子理解吧~如果不懂,欢迎追问哦
    代码如下:(参考浙江大学数据结构慕课代码)
void BuildMatch(char *pattern,int *match){
	int m = strlen(pattern); //pattern 长度
	match[0] = -1;//首位默认值
	Position i,j;//i为循环变量 ,j是中间变量
	
	for(i =1;i<m;i++){//pattern每一个字符都对应next值,因此进行for循环
		j = match[i-1] + 1;//灵魂赋值,在计算该位字符next值的时候,
		//如果借鉴前一位字符的next值,会事半功倍,
		//为什么要+1呢,假设计算abababc的next数组,
		//并且前三位aba已经得到,开始计算b的next值,
		//显然第三位a的next值为0,而此时比较第0+1位和当前位,都是b,则
		//可以得到当前位的next值为0+1;
		while( j>0 && (pattern[i]!=pattern[j]))
		//如果j对应的pattern字符存在前缀并且当前位无法和当前的前一位的
		//前缀的下一位匹配的话,继续回溯
			j = match[j-1]+1;
		//退出循环要么匹配成功,要么实在找不到前缀
		if(pattern[i]==pattern[j]) match[i] = j;
		//匹配成功就赋值
		else match[i] = -1; 
		//实在找不到就赋值-1
		printf("match[%d]=%d\n",i,match[i]);	
	}
	
}

网上还有很多教程,要是实在看不懂,就再看看别人的博客吧

KMP

Position KMP(char *string,char *pattern){
	int m = strlen(pattern);
	int n = strlen(string);
	if(n<m) return NOT_FIND;
	Position i = 0,j = 0,*match;
	match = (Position *)malloc(sizeof(Position)*m);
	BuildMatch(pattern,match);
	while(j<m && i<n){
		if(string[i] == pattern[j]){
			++i; ++j;
		}
		else if(j>0) j = match[j-1]+1; //回溯
		else ++i;
	}
	return (j==m )?(i-m):NOT_FIND;
	//按照数组下标来考虑的话,到m-1就可以认为匹配成功,但是
	//我觉得++i存在延迟,只要相等就会++;因此最后还是多加了个1;
	//因此用j==m进行判断。
} 

Whole code

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define NOT_FIND -1
typedef int Position;

void BuildMatch(char *pattern,int *match){
	int m = strlen(pattern);
	match[0] = -1;
	Position i,j;
	
	for(i =1;i<m;i++){
		j = match[i-1] + 1;
		while( j>0 && (pattern[i]!=pattern[j]))
			j = match[j-1]+1;
		if(pattern[i]==pattern[j]) match[i] = j;
		else match[i] = -1; 
		printf("match[%d]=%d\n",i,match[i]);
		
		/*if(i>1) {
		j = match[i-1]+1;
		if(pattern[i]==pattern[j]) match[i] = j;
		}
		else */
		
	}
	
}
Position KMP(char *string,char *pattern){
	int m = strlen(pattern);
	int n = strlen(string);
	if(n<m) return NOT_FIND;
	Position i = 0,j = 0,*match;
	match = (Position *)malloc(sizeof(Position)*m);
	BuildMatch(pattern,match);
	while(j<m && i<n){
		if(string[i] == pattern[j]){
			++i; ++j;
		}
		else if(j>0) j = match[j-1]+1;
		else ++i;
	}
	return (j==m )?(i-m):NOT_FIND;
} 

int main(){
	char string[] = "this is a not good choice";
	char pattern[] = "good"; //Obviously the answer is 14;
	Position answer = KMP(string,pattern);
//	printf("Position of pattern in string is:",answer); 
	printf("%d",answer);
//	printf("%d",strlen(pattern));
	return 0;
}

Result

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值