C语言实现字符串的模式匹配

news2024/11/20 8:30:13

一.模式匹配

字符串的模式匹配算法是用来查找一个字符串中是否存在另一个指定的字符串(即模式)的算法。常见的模式匹配算法包括暴力匹配算法、KMP算法、Boyer-Moore算法和Rabin-Karp算法。

  1. 暴力匹配算法:暴力匹配算法也称为朴素匹配算法,是最简单的一种字符串匹配算法。它从主串的第一个字符开始与模式串的第一个字符比较,如果相同,则继续比较后面的字符,直到发现不匹配的字符或者模式串完全匹配主串为止。
  2. KMP算法:KMP算法是由Knuth-Morris-Pratt三位作者共同提出的一种高效的字符串匹配算法。其核心思想是利用已经匹配过的信息,尽量减少无用的比较次数。具体实现方式是构建一个next数组,记录模式串中每个前缀子串的最长公共前后缀长度,然后根据next数组跳过一些已经匹配的字符,使得模式串移动的更加快速。
  3. Boyer-Moore算法:Boyer-Moore算法是由Robert S. Boyer和J Strother Moore在1977年提出的一种高效的字符串匹配算法。它通过预处理模式串中每个字符在模式串中出现的位置以及根据坏字符和好后缀规则来决定跳过多少个字符,以达到快速匹配的目的。
  4. Rabin-Karp算法:Rabin-Karp算法是由Richard M. Karp和Michael O. Rabin在1987年提出的一种基于哈希函数的字符串匹配算法。它利用哈希函数对主串和模式串进行哈希计算,并比较哈希值是否相等,从而判断是否匹配。同时,由于哈希函数具有良好的性质,可以在实际应用中获得较高的效率。

在我们考研408范围内需要掌握的也就是第一种暴力匹配算法和KMP匹配算法,今天我们重点来实现这两种算法。

二.暴力匹配算法

1.原理

暴力匹配算法的原理非常简单,它从主串的第一个字符开始,与模式串的第一个字符进行比较,如果相同,则继续比较主串和模式串的下一个字符。如果出现不匹配的情况,则将主串中的指针后移一位,重新从主串的下一个位置开始与模式串进行比较。

具体来说,暴力匹配算法的过程如下:

  1. 从主串的第一个字符开始,与模式串的第一个字符进行比较;
  2. 如果相同,则继续比较主串和模式串的下一个字符,直到模式串遍历完毕,返回匹配成功;
  3. 如果出现不匹配的情况,则将主串中的指针后移一位,重新从主串的下一个位置开始与模式串进行比较,直到主串遍历完毕。如果仍然没有找到匹配的子串,则返回匹配失败。

因为暴力匹配算法是一种最简单的字符串匹配算法,所以它的时间复杂度比较高,平均需要O(mn)次比较操作才能完成匹配,其中m和n分别表示模式串和主串的长度。但是,它也有一些优点,例如实现简单,代码易于理解和调试,适用于小规模的字符串匹配问题等。

2.示意图

3.C语言算法代码

//暴力匹配算法
int force_search(SString S,SString T){   //第一个字符串为主串,而第二个字符串为模式串
	int i=1,j=1;               //为了好匹配,让字符串从1开始,0的位置存储长度
	while(i<=S.length && j<=T.length){
		if(S.val[i]==T.val[j]){         //匹配到了第一个,继续向下匹配
			++i;
			++j;
		}
		else{
			i=i-j+2;              //指针回退重新匹配,这个代数式来源于笔算验证
			j=1;
		}
	}
	if(j>T.length)
		return i-T.length;        //匹配成功直至最后一位的下一位,j超出T的长度而i减去T的长度刚好就是匹配起始位置
	else 
		return 0;                  //不成功为了程序继续进行就输出0,我们认为知道匹配不成功就行
}

三.KMP匹配算法

1.原理

KMP算法的核心思想是在匹配过程中尽量利用已经匹配过的信息,从而减少比较次数。具体来说,它通过构建一个next数组,记录模式串中每个前缀子串的最长公共前后缀长度,然后根据next数组跳过一些已经匹配的字符,使得模式串移动的更加快速。

KMP算法的过程如下:

  1. 预处理:首先,我们需要构造出模式串p的next数组,用于保存模式串中每个前缀子串的最长公共前后缀长度。具体来说,从模式串的第二个位置开始,依次计算每个位置对应的next值:

(1)如果前缀的第一个字符和后缀的最后一个字符相等,则next值为前一个位置的next值+1; (2)如果前缀的第一个字符和后缀的最后一个字符不相等,或者已经没有前缀可以继续匹配了,则next值为0。

  1. 匹配:在匹配过程中,我们将主串s和模式串p的指针都初始化为0,然后依次比较它们的对应字符:

(1)如果当前字符匹配,则分别将指针后移一位,继续比较下一个字符; (2)如果当前字符不匹配,则根据next数组将模式串向右移动若干位,直到匹配或者模式串已经全部移动完成。

在KMP算法中,模式串的移动是根据next数组进行的。具体来说,当模式串中第j个字符与主串中第i个字符不匹配时,我们可以将模式串向右移动max(j-next[j],1)个位置,然后重新开始比较。这样,就能够充分利用已经匹配过的信息,避免重复比较。

总的来说,KMP算法的时间复杂度为O(m+n),其中m和n分别表示模式串和主串的长度。虽然KMP算法需要预处理出next数组,但是它对于稍大规模的字符串匹配问题具有较高的效率和较好的性能表现。

2.示意图

3.优化思路

KMP算法的优化主要有两个方面:

  1. 改进next数组的求法

传统的KMP算法中,next数组是通过暴力枚举前缀后缀来求解的,时间复杂度为O(m^2),其中m为模式串的长度。实际上,我们可以通过观察next数组的递推公式,发现next[j]的值与next[j-1]的值有关,因此可以通过递推求解next数组,时间复杂度为O(m)。

  1. 优化跳转操作

在匹配失败时,KMP算法通过next数组来确定新的匹配位置。然而,实际上我们可以通过分析next数组的定义,发现在某些情况下可以直接跳过一段字符,而不需要像传统的KMP算法那样只跳过一个字符。具体而言,如果s[i]!=p[j],且j>0,则可以直接将j跳转到next[j],而不是next[j-1]。

四.核心功能

1.求next数组

//求next数组
void get_next(SString T,int next[]){           //T是模式串
	int i=0,j=0;                                  //i用来表示当前匹配到的位置,j用来表示已经匹配好的最长前缀的末尾
	next[1]=0;              //next[0]不管,next[1]都是0
	while(i<T.length){
		if(j==0 || T.val[i]==T.val[j]){
			++i;
			++j;
			next[i]=j;
		}
		else
			j=next[j];
	}
}
/*这个部分一定要会手算*/

2.求nextval数组

void get_nextval(SString T,int nextval[]){
	int i=0,j=0;                                  //i用来表示当前匹配到的位置,j用来表示已经匹配好的最长前缀的末尾
	nextval[1]=0;              //next[0]不管,next[1]都是0
	while(i<T.length){
		if(j==0 || T.val[i]==T.val[j]){
			++i;
			++j;
			if(T.val[i]!=T.val[j])
				nextval[i]=j;
			else
				nextval[i]=nextval[j];
		}
		else
			j=nextval[j];
	}
}

3.KMP匹配

//KMP匹配算法
int KMP_search(SString S,SString T,int next[]){
	int i=1,j=1;
	while(i<=S.length && j<=T.length){  //修改循环条件,保证i和j都不超出字符串长度范围
		if(j==0 || S.val[i] == T.val[j]){ 
			++i;++j;                      //匹配到了第一个,继续匹配
		}
		else{
			j=next[j];                   //匹配失败后,模式串向右移动,i指针不动
		}
	}
	if(j>T.length) {                    //这一部分和暴力匹配一样
		return i-T.length;
	}
	else {
		return 0;	
	}
}

五.完整代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLEN 255
/*数据结构与算法中,常用的模式匹配算法包括暴力匹配算法和KMP匹配算法,今天在这里我们两个都尝试一下*/

//定义串的结构体
typedef struct SString{
	char val[MAXLEN];
	int length;
}SString;

//暴力匹配算法
int force_search(SString S,SString T){   //第一个字符串为主串,而第二个字符串为模式串
	int i=1,j=1;               //为了好匹配,让字符串从1开始,0的位置存储长度
	while(i<=S.length && j<=T.length){
		if(S.val[i]==T.val[j]){         //匹配到了第一个,继续向下匹配
			++i;
			++j;
		}
		else{
			i=i-j+2;              //指针回退重新匹配,这个代数式来源于笔算验证
			j=1;
		}
	}
	if(j>T.length)
		return i-T.length;        //匹配成功直至最后一位的下一位,j超出T的长度而i减去T的长度刚好就是匹配起始位置
	else 
		return 0;                  //不成功为了程序继续进行就输出0,我们认为知道匹配不成功就行
}

//求next数组
void get_next(SString T,int next[]){           //T是模式串
	int i=0,j=0;                                  //i用来表示当前匹配到的位置,j用来表示已经匹配好的最长前缀的末尾
	next[1]=0;              //next[0]不管,next[1]都是0
	while(i<T.length){
		if(j==0 || T.val[i]==T.val[j]){
			++i;
			++j;
			next[i]=j;
		}
		else
			j=next[j];
	}
}
/*这个部分一定要会手算*/

void get_nextval(SString T,int nextval[]){
	int i=0,j=0;                                  //i用来表示当前匹配到的位置,j用来表示已经匹配好的最长前缀的末尾
	nextval[1]=0;              //next[0]不管,next[1]都是0
	while(i<T.length){
		if(j==0 || T.val[i]==T.val[j]){
			++i;
			++j;
			if(T.val[i]!=T.val[j])
				nextval[i]=j;
			else
				nextval[i]=nextval[j];
		}
		else
			j=nextval[j];
	}
}

//KMP匹配算法
int KMP_search(SString S,SString T,int next[]){
	int i=1,j=1;
	while(i<=S.length && j<=T.length){  //修改循环条件,保证i和j都不超出字符串长度范围
		if(j==0 || S.val[i] == T.val[j]){ 
			++i;++j;                      //匹配到了第一个,继续匹配
		}
		else{
			j=next[j];                   //匹配失败后,模式串向右移动,i指针不动
		}
	}
	if(j>T.length) {                    //这一部分和暴力匹配一样
		return i-T.length;
	}
	else {
		return 0;	
	}
}


int main(){
	//初始化主串
	SString S,T;
	strcpy(S.val,"abcdefgabclmnzwxyzabcz");
	S.length=strlen(S.val);                //C语言不像python可以直接赋值,他需要strcpy函数过渡
	//初始化模式串
	strcpy(T.val,"abcz");
	T.length=strlen(T.val);
	//暴力搜索
	int pos1=force_search(S,T);
	printf("Force search result: %d\n",pos1);
	//KMP匹配搜索
	int next[T.length+1];
	int nextval[T.length+1];
	get_next(T,next);
	get_nextval(T,nextval);
	int pos2=KMP_search(S,T,nextval);
	printf("KMP search result: %d\n",pos2);
	
	return 0;
	
}

六.运行结果

9Qkl.jpg

实验结果与我们手算一致。

本次博客主要基于博主之前在CSDN平台的发布问答,根据采纳的答案进行学习总结。
有兴趣的可以前往查看原文回答KMP匹配算法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/653492.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自学黑客!一般人我劝你还是算了吧

一、自学网络安全学习的误区和陷阱 1.不要试图以编程为基础的学习开始学习 我在之前的回答中&#xff0c;我都一再强调不要以编程为基础再开始学习网络安全&#xff0c;一般来说&#xff0c;学习编程不但学习周期长&#xff0c;而且实际向安全过渡后可用到的关键知识并不多 一…

【Java之JAR包解析】(三)除核心包 rt.jar之外的其他JAR包~

JAR包解析之其他jar包 前言:one: access-bridge-64.jar:two: charsets.jar:three: cldrdata.jar:four: deploy.jar:five: jce.jar:six: jfr.jar:seven: jfxrt.jar:eight: jfxswt.jar:nine: jsse.jar:keycap_ten: localedata.jar11、management-agent.jar12、nashorn.jar13、plu…

开发人员Git仓库提交与合并

参考&#xff1a;git 的变基(rebase)和合并(merge)具体有什么分别阿&#xff1f; - 知乎 1、Git工作流 在使用Git Flow工作模式时&#xff0c;业界普遍遵循的规则&#xff1a; 所有开发分支从develop分支拉取。所有hotfix分支从master分支拉取。所有在master分支上的提交都必…

flstudio21.0.3中文版水果软件下载

FL Studio就是国人众所熟知的水果编曲软件&#xff0c;圈内用户习惯叫它“水果”。它是一个全能音乐制作环境或数字音频工作站&#xff08;DAW&#xff09;。FL Studio可以进行编曲、剪辑、录音、混音&#xff0c;让你的电脑变成全功能录音室&#xff0c;帮助你制作出属于自己的…

轻量服务器架设网站打开速度慢,如何加速?

轻量服务器非常适合流量适中的小、中型网站&#xff0c;虽作为轻量级主机包&#xff0c;但它一般与云服务器使用同样的 CPU、内存、硬盘等底层资源。只是&#xff0c;轻量服务器的资源(可用的存储空间、RAM 和 CPU等硬件/内存容量)更低&#xff0c;虽然这些对于较中、小的网站来…

GEN回零调试

一.根据motionstudio软件检测各部件完备&#xff1b; 二.调试点位模式的CPP测试程序 其中&#xff0c;配置文件如下&#xff1a; 回零相关&#xff08;就是轴状态同步&#xff09;&#xff1a; 下面是相关代码: // 例程 7-1 点位运动 //#include "stdafx.h" #inclu…

selenium自动化的时候网址重定向问题的解决思路

一、背景 因为我们系统是用企业微信扫码登录的&#xff0c;就输入网址 management-xxx.xxx.com以后&#xff0c;url就会重定向到企业微信授权的url &#xff1a;https://open.work.weixin.qq.com/wwopen/sso/3rd_qrConnect?statexxx&redirect_urimanagement-xxx.xxx.com …

如何制作数据可视化、数孪、安防、区域人流量识别+控制的项目?

制作与数据可视化、数字孪生、安防、区域人群识别和控制以及其他类似计划相关的项目需要仔细规划和执行。建议遵循以下通用框架来有效地开发这些项目&#xff1a; 定义项目目标&#xff1a;清楚地阐明项目目的和目标。确定要解决的具体问题、期望的结果以及衡量成功的关键绩效指…

vue3+ts+vite+electron打包exe

文章目录 一. 前言二. 准备写好的vue项目打包2.1 修改ts打包代码检测.这个比较烦人. 在package.json中 2.2 配置打包参数2.3 打包vue 三. 打包exe3.1 拉取electron官方demo3.2 下载打包插件3.3 在electron-quick-start项目中找到入口文件 main.js &#xff0c;修改打包的文件路…

差值结构的运动

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 让网络的输入有3个节点&#xff0c;训练集AB各由5张二值化的图片组成&#xff0c;让B全是0&#xff0c;让差值结构的5行分别有0&#xff0c;1&#xff0c;2&#xff0c;2&#xff0c;2个1&#xff0c;3列分别有1&#xff0c;3&#xff0…

知了堂Java V9.0重磅升级,真的很硬核!

“2023年&#xff0c;Java还值得学吗&#xff1f;” 说实话&#xff0c;Java自1995年诞生起&#xff0c;至今还难逢敌手&#xff0c;没有任何编程语言能够取代它的地位。不过随着互联网、计算机技术的发展&#xff0c;Java应用领域越来越广泛&#xff0c;因此也对掌握这门语言…

Vue全家桶(二):Vue中的axios异步通信

目录 1. Axios1.1 Axios介绍1.2 为什么使用Axios1.3 Axios API1.3 Vue使用axios向服务器请求数据1.4 Vue使用axios向服务器提交数据1.5 Vue封装网络请求 2. 使用Vue-cli解决Ajax跨域问题3. GitHub用户搜索案例4. Vue-resource 1. Axios 1.1 Axios介绍 Axios 是一个开源的可以…

flexible.js + rem 适配布局

什么是&#xff1a;flexible.js &#xff1f;&#xff1f; flexible.js 是手机淘宝团队出的移动端布局适配库不需要在写不同屏幕的媒体查询&#xff0c;因为里面js做了处理原理是把当前设备划分为10等份&#xff0c;但是不同设备下&#xff0c;比例还是一致的。要做的&#xf…

【亲测解决】import torch 出现段错误,报错信息 Segmentation fault

微信公众号&#xff1a;leetcode_algos_life import torch 出现段错误 【问题】【解决方案】 【问题】 安装pytorch-gpu版本&#xff0c;安装完成后&#xff0c;import torch发现报错直接返回&#xff0c;报错信息如下&#xff1a; Segmentation fault【解决方案】 Linux环境…

查看虚拟机网络IP和网关

查看虚拟网络编辑器和修IP地址: 查看网关&#xff1a; 查看windows:环境的中VMnet8网络配置(ipconfig指令): 查看linux的配置ifconfig: ping测试主机之间网络连通性: 基本语法 ping 目的主机&#xff08;功能描述&#xff1a;测试当前服务器是否可以连接目的主机) 应用实例 测…

一秒教你搞定前端打包上传后路由404的问题!

1、问题描述 前端实现权限管理后&#xff0c;本地路由跳转正常&#xff0c;打包上传线上出现前404找不到路由路径问题 报如下错误: 2、错误原因 打包之后根路径变化&#xff0c;前端没有将获取到的用户菜单权限中的component进行转换&#xff0c;导致上传后路径错误 3、解决…

Gurobi许可证获取并部署到Pycharm中

获取Gurobi许可证 海外版&#xff08;Gurobi&#xff09;~ 可略过 海外Gurobi地址但是就算用高校身份注册还是无法获取许可证图例 原因&#xff1b;学校的网关没有将本校的 IP 地址标注为学术机构&#xff0c;那么会出现 Error 303/305 错误&#xff0c;IP 验证不会成功&…

第三章_基于zookeeper实现分布式锁

实现分布式锁目前有三种流行方案&#xff0c;分别为基于数据库、Redis、Zookeeper的方案。这里主要介绍基于zk怎么实现分布式锁。在实现分布式锁之前&#xff0c;先回顾zookeeper的知识点。 知识点回顾 Zookeeper&#xff08;业界简称zk&#xff09;是一种提供配置管理、分布式…

NIFI1.21.0_Mysql到Mysql增量CDC同步中_日期类型_以及null数据同步处理补充---大数据之Nifi工作笔记0057

NIFI从MySql中增量同步数据_通过Mysql的binlog功能_实时同步mysql数据_根据binlog实现update数据实时同步_实际操作05---大数据之Nifi工作笔记0044 具体的,之前已经写过,如何在NIFI中实现MySQL的增量数据同步,但是写的简单了,因为,比如在插入的时候,更新的时候,仅仅是写死的某…

第五节 利用Ogre 2.3实现雨,雪,爆炸,飞机喷气尾焰等粒子效果

本节主要学习如何使用Ogre2.3加载粒子效果。为了学习方便&#xff0c;直接将官方粒子模块Sample_ParticleFX单独拿出来编译&#xff0c;学习如何实现粒子效果。 一. 前提须知 如果参考官方示例建议用最新版的Ogre 2.3.1。否则找不到有粒子效果的示例。不要用官网Ogre2.3 scri…