串的模式匹配

        以下介绍串的模式匹配,一是经典的模式匹配,就是主串移动,子串不断从头进行匹配;二是优化经典串对比的方式,即KMP算法,通过减少对比次数的方式使得时间复杂度更加少。

        经典串模式匹配:子串不断从头开始,只要匹配到错误的,子串回到第一个位置,主串回到匹配前的后一个位置,重新开始模式串匹配。

如下图,S串中 i 到第三个位置时,与T串中的 j 的第三个位置不符合,则就会回归到第二个位置,即 b 的位置,再跟T串进行比对。

 

 经典模式的思路很简单,就是通过不断从头开始比对,来对到符合子串,如果移动到符合时,就返回正确的标志。以下是以数组来定义串的。

i 回溯到原来位置+1,因为i = j,所以 i = i - j +1(i,j均从0开始)

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

int BT_algorithm(char* S, int s_len, char* P, int p_len)
{
	int i = 0, j = 0;
	while (i < s_len && j < p_len)
	{
		if (S[i] == P[j])
		{
			i++; j++;
		}
		else {
			i = i - j + 2;
			j = 1;
		}
	}
	if (j >= p_len) return 1;
	else  return 0; 
	
}
int main()
{
	char S[] = "ababcabcacbab";
	char P[] = "abcac";
	int s_len = strlen(S);
	int p_len = strlen(P);
    if (BT_algorithm(S, s_len, P, p_len))
	{
		printf("匹配成功!!!\n");
	}
	else {
		printf("匹配失败!!!\n");
	}
    return 0;
}

此法就是最简单的方法,也叫做暴力破解法。

接下来是对KMP算法的解析:

KMP算法是通过一个next[]来确定 i 回到的位置,经典的方法是每次都从下一个移动开始,但我们可以发现,一些重复的符号,明显串是不同的,因此没必要进行重新回溯。

KMP算法就是,S 中的 i 一直在移动,而子串 T 的 j 根据next[]函数来确定T对比的位置。

next[]函数,表明当模式中第j个字符与主串中相应字符“失配”时,在模式中需要重新和主串中该字符进行比较的字符的位置。 

以下通过一个例子来进行解释:这是其所有的回溯的位置 

当匹配的 j 为1时,即第一个位置匹配不成功,那么还是从第一个位置进行匹配;

当匹配的 j 为2时,即第二个位置匹配不成功,那么ab匹配错误,那么还是从第一个位置进行匹配。

当匹配的 j 为3时,即第三个位置匹配不成功,那么abc匹配错误,因为三个元素的前缀后缀不同,因此还是从第一个位置进行匹配。

当匹配的 j 为5时,即第五个位置匹配不成功,那么abcaa匹配错误,因为有前后缀a是一样的元素,因此下一次匹配从第二个位置进行匹配。

当匹配的 j 为7时,即第七个位置匹配不成功,那么abcaabb匹配错误,因为前后缀都有ab一样的两个元素,因此下一次匹配从第三个位置开始。

以此类推,就不一一进行解释了,KMP算法的手动推算方法可以看“王道” 老师的讲解,是通过两张纸进行拉就可以进行模拟了。大概的流程就如下图所示: 

 实现代码:

next函数:

void get_next(char* P,int p_len,int* next) {
	int i = 1,j = 0;
	next[1] = 0;
	while (i < p_len) 
	{
		if (j == 0 || P[i] == P[j])
		{
			++i; ++j;
			next[i] = j;
		}
		else {
			j = next[j];
		}
	}
}

主函数: 

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

#define MAXLEN 500
int KMP_algorithm(char* S, int s_len, char* P, int p_len,int* next)
{
	int i = 0,j = 0;
	while (i < s_len && j < p_len) 
	{
		if (j == 0||S[i] == P[j])
		{
			i++; j++;
		}
		else
			j = next[j];
	}
	if (j >= p_len)return TRUE;
	else return FALSE;
}
int main()
{
	char S[] = "ababcabcacbab";
	char P[] = "abcac";
	int s_len = strlen(S);
	int p_len = strlen(P);
	int next[MAXLEN] = { 0 };
	get_next(P, p_len, next);
	if (KMP_algorithm(S, s_len, P, p_len,nextval))
	{
		printf("匹配成功!!!\n");
	}
	else {
		printf("匹配失败!!!\n");
	}
	return 0;
}

KMP算法还有一个优化版,就是争对如果连续出现多个相同元素时,减少没必要的对比,下面定义为naxtval[]函数,这个就不做多解释了,看代码。

void get_nextval(char* P, int p_len, int* nextval) {
	int i = 1, j = 0;
	nextval[1] = 0;
	while (i < p_len)
	{
		if (j == 0 || P[i] == P[j])
		{
			++i; ++j;
			if (P[i] != P[j]) nextval[i] = j;
			else nextval[i] = nextval[j];
		}
		else {
			j = nextval[j];
		}
	}
}

总结:KMP算法相对在理解时,是有很大的困难的,我理解得也不是很透彻。

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值