字符串模式匹配 C语言解析

本文介绍了字符串模式匹配的基本概念,重点讨论了朴素模式匹配算法和KMP算法的工作原理,以及KMP算法如何通过避免主串回溯提高匹配效率。通过实例代码展示了这两种算法的实现过程和运行结果。
摘要由CSDN通过智能技术生成

一、前言

什么叫字符串模式匹配?在主串中找到与模式串相同的子串,并返回其所在位置。

子串——主串的一部分,一定存在

模式串——不一定能在主串中找到

字符串匹配模式有两种算法,第一种是:朴素模式匹配算法,第二种是:KMP算法

二、朴素模式算法 

主串长度为n,模式串长度为m

朴素模式匹配算法:将主串中所有长度为m的子串依次与模式串对比,直到找到一个完全匹配的子串,或所有的子串都不匹配为止。

最多对比 n-m+1 个子串

 当对比到主串第十个字符时,主串中的子串与模式串完全相同,返回是在第几个字符相同的数值。

相关代码:

typedef struct String
{
	char* ch;
	int length;
}SString;

int Index(SString S, SString T)
{
	int i = 1, j = 1;
	while (i <= S.length && j <= T.length)//i没有把主串走完,j没有把模式串走完
	{
		if (S.ch[i] == T.ch[j])
		{
			i++;
			j++;
			//如果相等,则向后继续比较字符
		}
		else
		{
			i = i - j + 2;//i= i - (j-1) + 1 回到主串上次开始的下一个位置
			j = 1;//返回到模式串开头
		}
	}
	if (j > T.length)//说明在主串找到与该模式串相同的子串
	{
		return i - T.length;//返回模式串在主串中第一次出现的位置
	}
	else
	{
		return 0;
	}
}

int main()
{
	SString S = { "abaabaabaabaabc",15 };
	SString T = { "abaabc",6 };
	int ret = Index(S, T);
	printf("%d ", ret);
	return 0;
}

运行结果:

运行的结果与上述分析的结果一致。 

三、KMP算法

KMP算法是朴素模式算法的优化,改进思路是:主串指针不回溯,只有模式串指针回溯

那么主串指针不回溯,模式串指针回溯是什么意思呢?这里用流程图来解释

 当我们模式串于主串进行匹配的时候,当比较到最后一个字符时才发现才发现是不匹配的。

那么我们可以确定,主串的前五个字符是与模式串是一致的,那我们就不再需要比较主串的前五个字符,同时我们因为知道主串的前五个字符,而模式串中开头也是已知的,因此我们可以从   k=3 的位置开始进行比较,如图:

主串指针 i 保持不变,模式串指针 j=3 ,跳过中间的几个字串,同时模式串也跳过开头的两个元素。这就是KMP算法的思想,利用模式串与主串匹配过程中发生失配的现象,保持主串中的指向不变,对模式串的指向进行调整。

 当最后一个字符再次不匹配的时候,再将主串指针i保持不变,模式串指针j=3。

那么,当我们在第五个字符不匹配的时候该怎么办呢?

 

当第五个字符不匹配的时候,模式串的前四个字符就是已知的,那我们可以保持i的位置不变,j=2

        

那么第四个字符开始不匹配呢?这是我们可以总结出规律:

 当第6个字符匹配失败时,可令主串指针 i 不变,模式串指针 j = 3

 当第5个字符匹配失败时,可令主串指针 i 不变,模式串指针 j = 2

 当第4个字符匹配失败时,可令主串指针 i 不变,模式串指针 j = 2

 当第3个字符匹配失败时,可令主串指针 i 不变,模式串指针 j = 1

 当第2个字符匹配失败时,可令主串指针 i 不变,模式串指针 j = 1

 当第1个字符匹配失败时,匹配下一个相邻的子串,模式串指针 j = 0

我们可以用一个next数组来对字符失配时,表示 j 该改变的位置

next[0]next[1]next[2]next[3]next[4]next[5]next[6]
011223

当模式串中的字符改变时,next数组中的内容也会改变,那我们怎么求得next数组呢?

next[1]都无脑写0,next[2]都无脑写1

其他next:在不匹配的位置前,划一根分界线,模式串一步一步往后退,直到分界线之前“能对上”,或模式串完全跨过分界线位置。此时j指向哪,next数组值就是多少。

相关代码:

typedef struct String
{
	char* ch;
	int length;
}SString;

//朴素模式算法
int Index_KMP(SString S, SString T,int next[])
{
	int i = 1, j = 1;
	while (i <= S.length && j <= T.length)//i没有把主串走完,j没有把子串走完
	{
		if (S.ch[i] == T.ch[j] || j==0)//当j=0,也要i++,j++
		{
			i++;
			j++;
			//如果相等,则向后继续比较字符
		}
		else
		{
			j = next[j];//返回到子串开头
		}
	}
	if (j > T.length)//说明找到子串
	{
		return i - T.length;//返回子串在主串中第一次出现的位置
	}
	else
	{
		return 0;
	}
}

int main()
{
	SString S = { "abaabaabaabaabc",15 };
	SString T = { "abaabc",6 };
	int next[7] = { 0,0,1,1,2,2,3 };
	int ret = Index_KMP(S, T,next);
	printf("%d ", ret);
	return 0;
}

运行结果:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值