数据结构 第五章 串(模式匹配算法)

在这里插入图片描述

🚀 【考纲要求】字符串模式匹配

一、暴力模式匹配算法

问题描述: 给定一个主串Text = "abcdabcd",再给定一个串Parten = "abg"。匹配算法要实现再主串Text是否含有子串Parten,若在主串Text中能找到子串Parten,则返回在子串Parten在主串Text的起始位置。

暴力匹配的思想
定义了两个变量。一个i和一个j,分别用于遍历数组Text和数组Parten
在这里插入图片描述

Text[i]中的内容与Parten[j]中的内容比较,如相等就不断的i++j++,依次继续往后比较,直到遇到Text[i] != Parten[j]时,表示这次从Text的第一个元素开始匹配不成功,此时就会ij的值就要进行会退操作。

j = 1;   //j回到原来的位置
i = i - j + 2;   //回到原来的位置加1的位置上 

这里的i = i - j + 2;,可以这样理解,i++j++是同时加的,首先j的值可以反应加了几次,即加了j-1次,先让j回到原来位置,即i = i- ( j- 1 ) = i - j + 1,由于是让i回到原来的位置加1的位置上 ,所以当Text[i]中的内容与Parten[j]中的内容不相等时,得让i = i - j + 2

在这里插入图片描述
一旦循环走完,就是j的值大于串Parten长度时,就表示已经找到了,返回子串Parten在主串Text的起始位置i-Parten.length。如何理解在主串Text的起始位置i-Parten.length?,和上述i回溯到原来的位置加1类似理解。匹配成功是j走完了怎个子串Parten,即j加了子串Parten的长度,对于其在主串Text的起始当然就是位置i-Parten.length,就是减去走了多少次。

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

// text存放主串  
// p存放匹配串

int Index(const char* text, const char* p) {
	int i = 1, j = 1;
	int legth_text = strlen(text)-1;  //减去空格
	int legth_p = strlen(p) - 1;  //减去空格

	while (i<= legth_text && j<= legth_p) {  //循环条件
		if (text[i] == p[j]) {   //相等就都++
			++i;
			++j;
			
		}
		else {  //不相等就归位
			i = i - j + 2;   //i指向开始匹配的后一个
			j = 1;           //j指向开始的第一个
		}
	}
	if (j > legth_p) {
		return i - legth_p;     //返回起始的位置
	}
	else {  //由于j<=strlen(p)条件循环结束,表示没有找到,就是j都走完了也没匹配上
		return 0;
	}
}


int main() {
	printf("%d\n", Index(" abcabcdfg", " ca"));   //3
	printf("%d\n", Index(" abcabcdfg", " abca")); //1
	printf("%d\n", Index(" abcabcdfg", " bcab")); //2
	return 0;
}

在这里插入图片描述

二、KMP算法

首先知道KMP算法有个大招就是next数组,这个数组会给出你每一次匹配失败后,j的返回位置,同时i保持不变。先不理解KMP算法的原理,感受它的过程后就会自然的理解。简单来看下KMP算法的过程。

当往后循环比较的时候,此时Text[i]中的内容与Parten[j]中的内容不相等,按着刚刚说的暴力解法应该此时让j回到原来的位置i回到原来的位置加1的位置上 。而我们的KMP算法有大佬求解了一个非常牛逼的next数组,该数组里面写好了每一次匹配失败之后j的返回位置!!!,所以在匹配失败的时候直接按着next中的内容返回写好的位置。(现在先不了解next数组的求法,后面会给出,这里先给出计算好的此次next数组,接着往下看它是如何运作的吧!)
在这里插入图片描述
此时Text[i]中的内容与Parten[j]中的内容不等,j按着next数组中的内容返回位置。此时不相等了,j按着next中的内容返回,即j=next[j]i不动,然后依次下去就能得到结果。(可以手动试试,这里不用管next数组怎么来的,后面会说,跟着过程体验一遍。)
在这里插入图片描述

#include<stdio.h>
#include<string.h>
int Index2(const char* text, const char* p) {
	int i = 1, j = 1;
	int next[7] = { -1, 0, 1, 1, 2, 3, 1 };
	int legth_text = strlen(text) - 1;  //减去空格
	int legth_p = strlen(p) - 1;  //减去空格

	while (i <= legth_text && j <= legth_p) {  //循环条件
		if (j==0 || text[i] == p[j]) {   //相等就都++
			++i;
			++j;

		}
		else {  //不相等就归位
			j = next[j];           //j由next数组确定
		}
	}
	if (j > legth_p) {
		return i - legth_p;     //返回起始的位置
	}
	else {              //由于j<=strlen(p)条件循环结束,表示没有找到,就是j都走完了也没匹配上
		return 0;
	}
}

int main() {
	printf("%d\n", Index2(" abababcd", " ababcd"));   //3
	return 0;
}

在这里插入图片描述

如上代码,我们得到了同样的结果,其实这就是采用KMP算法进行模式匹配的,只不过到现在为止,还没有真正的了解KMP算法的核心步骤,其核心就是next数组的求法,现在就开始学习其是如何求解next数组的。

三、KMP算法的核心----求next数组

这里的求解next的过程讲解只是一个浅显的理解,更多的是应试,要想深刻理解里面的求解步骤,及其原理过程的话,推荐以下一个视频,讲解的十分好。

【【完整版】终于有人讲清楚了KMP算法,Java语言C语言实现】
https://www.bilibili.com/video/BV1UL411E7M8/?share_source=copy_web&vd_source=c0ec1c9d72932bb83637199ead965f65

Parten[j]为a、b、a、b、c、d,next数组就是基于待匹配串求出来的,求next的关键:找到匹配成功部分的两个相等的真子串(不包含本身),以第一个字符开始,一个以j-1下标处的字符结尾。

  • next数组第一个和第二个只写-1、0
  • 当在Parten的第3个元素a处发生不匹配时候,找Parten的第三个元素前序列的两个相等的真子串(找到匹配成功部分,即第3个元素前都是匹配的),无相等的真子串,写0。
  • 当在Parten的第4个元素b处发生不匹配时候,找Parten的第四个元素前序列的两个相等的真子串,即a子串和a子串,字串的元素个数为1,就写1。
  • 当在Parten的第5个元素c处发生不匹配时候,找Parten的第五个元素前序列的两个相等的真子串,即a b子串和a b子串,字串的元素个数为2,就写2。
  • 当在Parten的第6个元素d处发生不匹配时候,找Parten的第六个元素前序列的两个相等的真子串,无相等的真子串(可能会觉得有a b子串和a b子串,但是要求以第一个字符开始,一个以j-1下标处的字符结尾,此时j-1下标处的字符为c),写0。

**这样就求出来了next数组!!!!最后还需要将next数组中的数值都加1,就真的得到了最后的next数组。**即为[0,1,1,2,3,1];也就是上面我们使用的。这是手动求next数组,但是对于代码来说,是推导了其数据过程,最后得出其递推的公式,然后再编写求解next数组的代码的。
在这里插入图片描述
代码实现求next数组:

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

void get_next(const char* p, int next[]) {
	int i = 1, j = 0;
	next[0] = -1;
	next[1] = 0;
	int len = strlen(p)-1;  //减去第0个位置处的长度
	while (i<len) {
		if (j == 0 || p[i] == p[j]) {
			i++;
			j++;
			next[i] = j;    //p[i] == p[j]时,next[j+1]=next[j]+1
		}
		else {
			j = next[j];    //令j = next[j],循环继续
		}
	}
}

int main() {
	//printf("%d\n", Index2(" abababcd", " ababcd"));   //3
	int next[7];
	get_next(" ababcd", next);
	printf("求得的next如下:\n");
	for (int i = 1; i < 7;i++) {
		printf("%d ", next[i]);
	}
	printf("\n");
	return 0;
}

在这里插入图片描述
现在只需要把函数int Index2(const char* text, const char* p)中修改成使用上述函数求next值就可以了,到这里KMP算法就完成了!!!

	int next[7] = { -1, 0, 1, 1, 2, 3, 1 };
	//改成
	int Index2(const char* text, const char* p, int next[] ) //从外部传进函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值