串的运用--模式匹配--KMP算法(C语言)

模式匹配(C语言)

用于串学习中的字符串匹配
从主串T中匹配与字串(模式)相同的的字符,返回主串与模式匹配的首个字符的下标。
以下介绍两种算法:

//定义串格式 0号单元存放串的长度
typedef unsigned char SString[MAXSIZE + 1];  

BF算法

BF算法是运用双指针i,j;i从主串的第pos个下标开始,j从模式串的第一位开始,将模式串与主串比较,依照指针i,j一一比较,若i,j指向的单位元素相等则进行i指向主串下一位置(i++),j指向模式串下一元素(j++)。若不相等则i指针返回此次主串与模式串比较的首次元素的下一个位置(i=i-j+2),j返回模式串首位(j=1),进行再次比较,直到模式串与主串上的部分序列完全相同,则匹配完成返回此次i开始的主串位置(i-T(0)),否则返回0。

int StrIndex_BF(SString S, SString T,int pos)
{
	//入口参数:主串S 模式串T 开始匹配的下标pos
	//返回参数:子串T在主串S中的第pos个字符之后的位置,若不存在返回0
	//限制条件:T非空  1<=pos<=Strlength(S)
	int i = pos, j = 1;
	while (i <= S[0] && j <= T[0])
	{
		if (S[i] == T[j]) { ++i, ++j; }
		else { i = i - j + 2; j = 1; }           //回溯
	}
	if (j > T[0])  return i - T[0];
	else return 0;
}

在最好的情况下时间复杂的为O(n+m)
在最差的情况下时间复杂的为O(n*m)
n,m分别为主串和字符串的长度

如图:pos=1(0位置储存串长度)
在这里插入图片描述

BF算法中因为回溯的原因导致了算法在一定情况下不适合。

KMP算法(BF算法的优化)

在上述的BF算法中,由于回溯导致了时间复杂度增加,在BF算法的图解中可以知道第2,4,5趟匹配是无效的。
如图进行优化:

在这里插入图片描述

在优化的图中进行了"滑动",则不是模式串右移一位,而是根据已经与主串比较的模式串的序列的规律进行滑动(由规律减少了回溯的次数)。
规律:
在模式串中查看已经匹配的子串序列,在其字串中查找最大公共前后缀,将j指针所指的位置指向最大公共前后缀的长度+1的模式串下标。
如:上图
第一趟:j=3,已匹配的子串为ab,没有最大公共前后缀,则最大公共前后缀的长度为0+1,即下一趟开始的时候j=1。
第二趟:j=5,已经匹配的子串为abca,最大公共前后缀的长度为1+1,即下一趟开始的时候j=2。
*[最大公共前后缀]:字符串前面由字符串头部开始不到尾部的子序列,字符串不由头部开始尾部结束的子序列叫做前缀和后缀。
如 :ababa的最大公共前后缀为aba,前缀为abaab,后缀为ababa
可知滑动的长度与主串没有关系,只跟模式串的j指针的所指的模式串下标有关,即每一个模式串的元素(下标)都对应一个相应的滑动值。
将滑动的长度即最大公共前后缀的长度存在next数组中。
优化后得代码为:
kmp算法代码

int StrIndex_KMP(SString S, SString T, int pos,int next[])
{
	int i = pos, j = 1;
	while(i <= S[0] && j <= T[0]) {
		if (j == 0 || S[i] == T[j]) { i++; j++; }//继续比较后继字符
		else j = next[j];  //模式串向右移动
	}
	if (j > T[0])  return i - T[0];
	else return 0;
}

next数组

(最大公共前后缀可得)
next值仅取决于模式串本身,而和相匹配的主串无关,可从分析其定义出发用递推的方法求得next函数值。
next[1]=0

设已经知道了next[j]=k
则模式串中下标1到k-1 与下标j-k+1到j-1元素一一对应
则next[j+1]=?存在两种可能

  1. T[j]=T[k],则next[j+1]=next[j]+1
  2. T[j]≠T[k],则重新(查找公共前后缀(此时可把求next函数值的问题看成是一个模式匹配的问题,整个模式串既是主串又是模式串,而当前在匹配的过程中,已有tj–k+1=t1,tj–k+2=t2,…,tj−1=tk−1)此时已经知道了T[k]≠T[j],则判断T[next[k]]=T[j]是否成立,如成立则next[j]=next[k]+1。不成立则依次类推,直到T[j]与某个字符相等,否则next[j]=1。

如图:
在这里插入图片描述
get_next代码:

void get_next(SString T, int *next)
{
   //求模式串的next函数值并存入数组next中
   int i = 1, j = 0; next[1] = 0;
   while (i < 9)
   {
   	if (j == 0 || T[i] == T[j]) { ++i; ++j; next[i] = j; }
   	else j = next[j];
   }//get _next;
}

算法复杂度:O(m)

nextval数组

next函数值的优化
如“aaaab”在和主串“aaabaaaab”匹配时,当i=4、j=4时S[4]≠T[4],由next[j]的指示还需进行i=4、j=3,i=4、j=2,i=4、j=1这3次比较。
实际上,因为模式中第1~3个字符和第4个字符都相等,因此不需要再和主串中第4个字符相比较,而可以将模式连续向右滑动4个字符的位置直接进行i=5、j=1时的字符比较。这就是说,若按上述定义得到next[j]=k,而模式中T[j]=T[k],则当主串中字符S[i]和T[j]比较不等时,不需要再和T[k]进行比较,而直接和Tnext[k]进行比较,换句话说,此时的next[j]应和next[k]相同。由此可得计算next函数修正值的算法
如图:在这里插入图片描述

void get_nextval(SString T, int nextval[])
{
	//求模式串T的next函数修正值并存入数组nextval中
	int i = 1, nextval[1] = 0, j = 0;
	while (i < T[0])
	{
		if (j == 0 || T[i] == T[j]) {
			++i; ++j;
			if (T[i] != T[j])nextval[i] = j;
			else nextval[i] = nextval[j];
		}
		else j = nextval[j];
	}
}//get nextval;
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值