实验二:使用KMP算法实现字符串的匹配

2023-11-19

1.实验目的

熟练的掌握数据结构中串这种数据类型;学会使用相较于朴素的模式识别算法更加先进的KMP算法进行识别和匹配。

同时,在数据结构试验之中熟悉和了解串的性质和使用方法。

2.实验要求

输入:通过命令行参数输入原字符串和模式字符串。

输出:   (1)命令行参数不正确输出字符串ERROR_01;

        (2)如果查找到模式串,输出关键字在字符串中的位置(计数从1开始);

        (3)如果未找到模式串则输出-1 。

实例:           输入:“select * from duaadual” “dual”;输出:19

             输入:“select * from duaadual” “duel”;输出:-1

代码质量要求:

1、优选C语言,禁止直接调用C++ STL库;

2、除循环变量外,其它变量命名使用有明确含义的单词或缩写,不建议使用拼音;

3、禁止出现魔鬼数字;                   4、添加必要的程序注释;

5、统一代码格式,例如:{}和空行;6、变量初始化,不要依赖默认赋值;

7、入参检查,“外部输入输入不可靠”,指针判空(一级指针、二级指针……),循环变量上下限;

8、malloc与free配对。

3.实验原理

KMP算法

  1. Next数组的求解方法:
  • 最长相等的前后缀

字符串 abcdab
前缀的集合:{a,ab,abc,abcd,abcda},前缀为从前边开始计字符。
后缀的集合:{b,ab,dab,cdab,bcdab},后缀为从后边开始记字符,正常顺序。

则其中相等的最长的前后缀为ab

  • 进行求解next数组的过程

 

最后形成公式:

 

  • 具体的代码:

 

核心为:如果相等,则直接加一;如果不相等,那么让k=next[k];(迭代之后形成的)

  1. 寻址的过程

 

ERROR_01的判定

针对输入的aggc进行判断,当其为2时,认为入参正确,不做响应。

                                           当其不为2时,认为入参出现问题,返回ERROR_01。

4.运行结果

 

5.问题与思考

本次实验之中存在最后一个用例长时间无法通过的问题。(用例为超过8bit存储长度的用例)

原因:

在对于目标串进行处理时,将目标串长度存在了目标串的[0]位。

相当于将一个数字存进了一个字符型的空间之中。Char型变量空间长度为8。当其为 8位1时,其可以存储的最大数字为255.当其长度超过255时,数字将脱离控制。

改进方法:

直接调用串结构之中的Curlen代替目标串[0]位存储目标传长度.

#include<stdio.h>
#include<malloc.h>
#include<string.h>
#define _CRT_SECURE_NO_WARNINGS_
#define OK 2
#define correct 3//正确的aggc个数
#define ERROR -1
#define ERROR_01 -3
#define ERROR_02 -4
#define ERROR_03 -5
#define OVERFLOW -2
#define big 1
#define change 1//用于将从0开始的下标变成从1开始的下标
#define CHANGE 2//中文字符转换的问题
#define equal 0
#define small -1 //以上三个时strcompare的特供
#define TRUE 1
#define FALSE 0
#define Status int//用数值返回状态,状态值如上所示。
#define ElemType char//串一半都是字符串。
#define InitSize 500
#define IncreaseSize 10 
#define resultlen 2//可变长参数result的长度,1:status,2:数值。
#define statu 0
#define index 1
#define data 2
#define RESULT_SIZE 2//result的长度
#define position 0//CMP进行比较时本程序从0开始进行比较,之后可以进行修改。
#define none 0//未输入
//使用CMP算法进行处理
typedef struct str
{
	ElemType* base;
	int CurLen;
}String;//声明字符串结构体,主要是给Station和Target用的,base的第一个base[0]用于存放字符串总长度Curlen(在next等地方用于占位)
//void visit(String* target)
//{
//	for (int i = 0; i <= target->CurLen; i++)
//	{
//		printf("%c\, %d\n\n\n", target->base[i],i);
//	}
//}
int max(int first, int secoend)
{
	if (first < secoend) return secoend;
	else return first;
}//取前一项,后一项之中更大的一项返回
Status STRCOPY(char* target, String* result)
{
	if (!target) return ERROR;
	if (!result) return ERROR;
	int Total_Length = strlen(target);//求解总的长度
	int i = 0;
	result->base[i] = Total_Length;//第一位存放总长度,之后有效数据从1开始,便于后边的理解
	for ( i = 1; i <= Total_Length; i++)
	{
		result->base[i] = target[i-1];
	}
	result->CurLen = Total_Length;
	result->base[i] = '\0';
	return OK;
}//重置非标准字符串格式为标准字符串格式
int Strlen(char* input)
{
	if (!input) return ERROR;
	int output=0;
	while (input[output])
	{
		output++;
	}
	return output;
}//定义函数求解字符串的长度
Status Strassign(String* Target, char* Data)
{
	if (Target) free(Target);
	int i = Strlen(Data);//判断总的长度,Curlen
	if (!i)
	{
		Target->base = NULL;
		Target->CurLen = 0;
	}//输入的DAta之中没有东西,空串
	else
	{
		Target->base = (char*)malloc((i + data) * sizeof(char));
		if (!Target->base) return OVERFLOW;
		STRCOPY(Data, Target);//将data导到Target之中
		Target->CurLen = i;//长度为i
	}
	return OK;
}//用于将字符串进行处理
int StrCompare(String* stringa, String* stringb)
{
	if (!stringa || !stringb) return ERROR;
	if (stringa->CurLen > stringb->CurLen) return big;
	if (stringa->CurLen < stringb->CurLen) return small;
	if (stringa->CurLen == stringb->CurLen)//当长度一样时,一直寻找到不一样的,然后比较
	{
		int flag = 1;
		int i = 1;
		while (flag && i <= max(stringa->CurLen, stringb->CurLen))
		{
			if (stringa->base[i] == stringb->base[i]) flag = flag;
			else flag = 0;
			i++;
		}
		if (i == max(stringa->CurLen, stringb->CurLen)+change) return equal;
		else
		{
			if (stringa->base[i] > stringb->base[i]) return big;
			else return small;
		}
	}
	return ERROR;
}//进行比较,输入两个字符串,A》B返回big A=B返回equal A《B返回small
Status StrClear(String* input)
{
	if (!input) return ERROR;
	input->CurLen = 0;
	free(input->base);
	input->base = (char*)malloc(input->CurLen * sizeof(char));
	return OK;
}//定义清空字符串操作
Status get_next(String* Target, int* next)
{
	if (!Target) return ERROR;
	if (!Target->base) return ERROR;
	int i = 1;//next的下标,从1开始。0位用于存储总串长度。
	next[i] = 0;
	next[0] = Target->CurLen;//0位置储存T串的总长度
	int k = 0;//k为可以满足条件的最大长度值。
	while (i < Target->CurLen)
	{
		if (k == 0 || Target->base[i] == Target->base[k])
		{
			i++;
			k++;
			next[i] = k;
		}//如果Pj=Pk或着直接为零,那么,直接加一就行了。
		else k = next[k];//当不满足上述条件的时候,直接进行处理,让k取到next【k】。
	}
	return OK;
}//定义取用下一位的操作
Status Index_CMP(String* Station, String* Target, int pos,int* result)
//S为目标串,T为模式串,pos为起始比较位置,result为返回的值参数。
{
	if (!Station || !Target||!result) return ERROR;
	if (pos<0 || pos>Station->CurLen) return ERROR;
	if (StrCompare(Station, Target) == small)
	{
		result[statu] = FALSE;
		return FALSE;
	}//用于比较目标串和模式串的长度,目标串短就不用比了
	int* next = (int*)malloc((Target->CurLen+data) * sizeof(int));
	memset(next, 0, (Target->CurLen + data) * sizeof(int));
	if (!next) return OVERFLOW;//next数组角标从1开始,方便后边的使用,便于理解,故+data
	get_next(Target, next);
	int i = pos;
	int k = 1;
	while (i <= Station->CurLen && k <= Target->CurLen)
	{
		if (k == 0 || Station->base[i] == Target->base[k])
		{
			++k;
			++i;
		}//如果为0,那么相当于向后移动一位,如果两个相等,向后移动一位是属于正常的现象。
		else
		{
			k = next[k];//如果匹配失败,直接进入next[k]位置进行匹配与判断。这个时候i是不动的。
		}
	}
	free(next);
	if (k > Target->CurLen)
	{
		result[statu] = OK;
		result[index] = i - Target->CurLen;//现在比到了最后一位。要回到第一位并返回第一位的值。
		return OK;
	}
	else
	{
		result[statu] = FALSE;
		return FALSE;//当k无法大于T串长度时,认为其没有办法完成全部T串的比较。直接失效。
	}
}//执行KMP算法对于字符串进行分析拿到首位地址
Status IsError01(int aggc,char** argv)
{
	if (!argv) return ERROR;
	if (aggc ==correct) return FALSE;
	else return TRUE;
}//判断是否命令行参数错误。
Status InitString(String* input,int curlen)
{
	if (!input) return ERROR;
	input->CurLen = curlen;
	input->base = (char*)malloc((input->CurLen+data) * sizeof(char));
	return OK;
}//初始化字符串结构体。
int final_Index_Package(int aggc, char** argv,int *result)
{
	if (!result) return ERROR;
	if (!argv) return ERROR;
	if (IsError01(aggc,argv))
	{
		printf("ERROR_01");
		return ERROR_01;
	}//判断是否命令行参数错误.
	String* Station = (String*)malloc(sizeof(String));
	if (!Station) return OVERFLOW;
	InitString(Station,strlen(argv[1]));//用于存放目标串
	String* Target = (String*)malloc(sizeof(String));
	if (!Target) return OVERFLOW;
	InitString(Target,strlen(argv[2]));//用于存放模式串
	STRCOPY(argv[1], Station);//将输入的命令行参数存放进目标串中
	STRCOPY(argv[2], Target);//将输入的命令行参数存放进模式串中
	//visit(Station);
	//visit(Target);
	Index_CMP(Station, Target,position,result);//CMP算法
	free(Station);
	free(Target);
	return 0;
}//进行KMP寻址之前的调用与初始化函数
Status Ans(int* result)
{
	if (!result) return ERROR;
	switch (result[statu])
	{
		case FALSE:
		{
			printf("-1");
			return FALSE;//FALSE表示未找到
		}
		case OK:
		{
			if (result[index]!=none)
			{
				printf("%d", result[index]);
			}
			else //当data项为none时,模式串没有任何输入,返回ERROR_01
			{
				printf("ERROR_01");
			}
			return OK;
		}
	}
	return ERROR;
}//对答案进行输出

int main(int argc, char** argv)
{
	int* result = (int*)malloc((RESULT_SIZE) * sizeof(int));//result用于存放结果
	memset(result, 0, RESULT_SIZE);
	if (!result) return OVERFLOW;
	final_Index_Package(argc,argv, result);//用于寻找字符串的主函数
	Ans(result);//用与输出结果
	free(result);
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

实验二:使用KMP算法实现字符串的匹配 的相关文章

  • copy_from_user() 错误:目标大小太小

    我正在为内核模块编写 ioctl 处理程序 我想从用户空间复制数据 当我编译禁用优化的代码时 O0 gflags 编译器返回以下错误 include linux thread info h 136 17 error call to bad
  • 非模板函数中的尾随返回类型[重复]

    这个问题在这里已经有答案了 我见过有人使用以下语法来实现函数 auto get next gt int 代替 int get next 我理解两者 并且我知道尾随返回类型语法对于使用 decltype 的模板代码很有用 就我个人而言 我会避
  • 并行运行多个任务

    我有一个代理列表 每个代理都会访问不同的站点并从站点中提取所需的数据 目前它一次只做一个 但我希望同时运行 10 20 个任务 这样它就可以一次性从 20 个站点下载 而不是只下载一个 这是我目前正在做的事情 private async T
  • C++中类成员函数相互调用有什么好处?

    我是 C 新手 我发现下面的编程风格对我来说很有趣 我在这里写了一个简化版本 include
  • Visual Studio 2013 调试器显示 std::string 的奇怪值

    我有一个大型的 cmake 生成的解决方案 其中包含许多项目 由于某种原因 我无法查看字符串的内容 因为根据调试器 Bx Buf含有一些垃圾 text c str 正确返回 Hello 该问题不仅仅发生在本地字符串上 返回的函数std st
  • 我担心我添加了太多接口

    我正在构建我的领域模型并继续重构它 正如我所做的那样 我发现我喜欢接口 因为它允许我根据接口为具体类型创建可重用的方法 控制器 视图 但是 我发现每次向域实体之一添加新属性时 我都会创建一个接口 例如 我有一个会员状态从抽象继承的对象Ent
  • 加载 QPixmap 数据的更好方法

    更好的方法来做到这一点 没有QImage QImage image width height QImage Format RGB888 memcpy image bits m frameRGB gt data 0 height width
  • 公交车公共交通算法

    我正在开发一个可以查找公交路线的离线 C 应用程序 我可以提取时间表 巴士 路线数据 我正在寻找适用于基本数据的最简单的解决方案 可以使用什么算法来查找从巴士站 A 到巴士站 B 的路线 是否有适用于 C Java 的开源解决方案 数据库的
  • 从图像创建半透明光标

    是否可以从图像创建光标并使其半透明 我目前正在拍摄自定义图像并覆盖鼠标光标图像 如果我可以将其设为半透明 那就太好了 但不是必需的 销售人员喜欢闪亮的 目前正在做这样的事情 Image cursorImage customImage Get
  • X 轴和 Z 轴上的 Quaternion.Slerp,无 Y 轴

    I am trying to rotate the Player about X Y and Z axis The Y axis should not move from last angle Example if I rotate 45
  • 使用 STL 流时如何格式化我自己的对象?

    我想将我自己的对象输出到 STL 流 但具有自定义格式 我想出了这样的东西 但由于我之前从未使用过 locale 和 imbue 所以我不知道这是否有意义以及如何实现 MyFacet 和operator 所以我的问题是 这是否有意义以及如何
  • 为什么连续抛出 2 个异常不会生成无法访问的代码警告?

    为什么以下代码行不会创建编译器警告 void Main throw new Exception throw new Exception 据我所知 编译器应该通知您无法到达第二个抛出异常 这显然是一个编译器错误 它是在 C 3 0 中引入的
  • 使用任一默认捕获模式时,这是通过复制捕获还是 (*this) 通过引用捕获?是一样的吗?

    当我看到以下工作时我有点困惑 struct A void g void f g 但后来我发现this https stackoverflow com a 16323119 5825294答案非常详细地解释了它是如何工作的 本质上 它归结为t
  • MINIX内部碎片2

    我正在用 C 语言编写一些软件 它递归地列出给定目录中的所有文件 现在我需要计算出内部碎片 我花了很长时间研究这个问题 发现 ext2 上的内部碎片只发生在最后一个块中 我知道理论上你应该能够从索引节点号获得第一个和最后一个块地址 但我不知
  • fgets溢出后如何清除输入缓冲区?

    当输入字符串超出其预定义限制时 我遇到了 fgets 的小问题 以下面的例子为例 for index 0 index lt max index printf Enter the d string index 1 if fgets input
  • 为什么这个位图图像在加载后会改变大小?

    快速提问 我有这个1000 1000位图图像 我使用这个例程来加载它 private BitmapSource initialBitmap new BitmapImage new Uri C Users Desktop Original b
  • C++网络序列化[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一种将 C 数据包序列化为网络流的解决方案 我在这里看到很多帖子提到人们 ACE 谷歌协议缓
  • 如何将模型绑定到动态创建的类 nancyfx

    首先感谢任何愿意查看我的问题的人 我对 Nancyfx 还很陌生 在尝试将 JSON 有效负载绑定到动态创建的类时遇到问题 我按照这篇文章中的代码动态创建了该类 在C 中动态创建一个类 https stackoverflow com que
  • NHibernate:无状态会话错误消息无法获取代理

    我正在使用 nHibernate 无状态会话来获取对象 更新一个属性并将对象保存回数据库 我不断收到错误消息 无状态会话无法获取代理 我在其他地方有类似的代码 所以我不明白为什么这不起作用 有谁知道问题可能是什么 我正在尝试更新Screen
  • 如何使用 Microsoft Graph API 更新 MailboxSettings

    我想从不同的日历更新邮箱设置 如何构建可以通过 Microsoft Graph 更新 MailboxSetting 的请求 这是我的代码示例 但有例外 代码示例 User obj GraphServiceClient Users roomC

随机推荐

  • Vue自定义InputNumber 计数器组件

    1 为什么要自己封装一个InputNumber 计数器组件 因为原始的el element的el input number组件有问题 原生组件能输入英文 不能限制只能输入数值 原始组件能通过键盘上的删除按钮 将数据全部删除 若提交表单的话
  • Vue3.0的新特性(8)Suspense

    Suspense是Vue3推出的一个内置组件 它允许我们的程序在等待异步组件时渲染一些后备的内容 可以让我们创建一个平滑的用户体验 Vue中加载异步组件其实在Vue2 x中已经有了 我们用的vue router中加载的路由组件其实也是一个异
  • 风口之上,为什么说区块链能改变世界?

    近几年 区块链被炒得热火朝天 各大媒体都能看见区块链的身影 其中很多甚至开设了区块链或比特币频道 你甚至很难找到一个科技或垂直网站 在标题中没有至少一篇意为 区块链改变世界 的文章 它们想传达的是 比特币和其他加密货币底层的区块链技术是如何
  • Idea 报Error:java:无效的源发行版13

    首先打开自己的项目 点击File gt Settings进入界面找到如图位置 并将相信应位置设置成自己的安装版本号 本人使用 1 8版本 别忘了点击OK 下一步 点击File选择Project Structure 进入 还是看自己的安装版本
  • java如何查询并显示一个表,如何从表中获取数据并将其显示在Java的另一个表中?...

    将数据从一个表复制到另一个表的Netbean项目 希望这可以帮助 import javax swing table DefaultTableModel To change this license header choose License
  • 概率论【离散型二维变量与连续性二维变量(下)】--猴博士爱讲课

    6 连续型二维变量 下 1 7 求边缘分布函数 边缘概率密度 边缘概率密度 2 7 求边缘密度函数 边缘概率密度 3 7 判断连续型二维变量的独立性 F x y Fx X Fy Y 那么X Y互相独立 f x y fx X fy Y 那么X
  • 【论文阅读 08】Adaptive Anomaly Detection within Near-regular Milling Textures

    2013年 太老了 先不看 比较老的一篇论文 近规则铣削纹理中的自适应异常检测 1 Abstract 在钢质量控制中的应用 我们提出了图像处理算法 用于无监督地检测隐藏在全局铣削模式内的异常 因此 我们考虑了基于全局傅里叶的方法和局部剪切波
  • php判断2个多维数组是否相同,PHP如何判断一个数组是一维还是多维

    什么叫多维数组呢 多维数组 本质上是以数组作为数组元素的数组 二维数组又称为矩阵 一个数组的元素如果是一维数组 那么我们就称这个数组是二维数组 怎么判断一个数组是否是一维数组呢 通过count 函数 int count mixed var
  • 【FPGA】:频率测量

    转载 1 FPGA频率测量的三种方法 直接测量法 间接测量法 等精度测量法
  • Go中sync 包的 Once 使用

    文章目录 背景 One 简介 示例 注意 源码解读 背景 在系统初始化的时候 某些代码只想被执行一次 这时应该怎么做呢 没有学习 Once 前 大家可能想到 声明一个标识 表示是否初始化过 然后初始化这个标识加锁 更新这个标识 但是学会了
  • .net IOC之Spring.Net

    一 开发环境 编译器 VS2013 Net版本 net framework4 5 二 涉及程序集 Spring Core dll 1 3 Common Logging 三 开发过程 1 项目结构 2 添加Person cs namespac
  • 数码管电子时钟

    文章目录 前言 一 回顾数码管 二 任务描述 三 系统框图 四 模块调用 五 模块原理图 六 工程源码 6 2 时钟计数模块代码 6 2 数码管驱动模块代码 6 3 顶层模块代码 七 仿真测试 7 1 测试代码 7 2 仿真结果 八 管脚信
  • networkmanager无法打开

    中午登录ubuntu刚要连接无可线发现个的问题 无线的图标不见了 这可肿么办啊 怎么找都找不到 开始想系统还原 后来发现还挺麻烦的 毕竟菜鸟 系统方面的还不怎么懂 幸好有两台电脑 可以google 唉 最近两天google也不正常 今天也不
  • Llama 美洲鸵(大羊驼)改进之一:均方层归一化RMSNorm

    Layer Normalization LayerNorm Root Mean Square Layer Normalization RMSNorm 原理 对特征张量按照某一维度或某几个维度进行0均值 1方差的归一化 操作 LayerNor
  • 神经网络控制系统的特点,神经网络控制的优点

    什么是神经网络控制 神经网络控制技术是一项复杂的系统控制技术 一般应用在变频器的控制中 它是通过对系统的辨识 运算后对变频器进行控制的一种新技术 而且神经网络控制可以同时控制多个变频器 所以应用在多个变频器级联控制中比较合适 谷歌人工智能写
  • angularjs官方教程中的两处错误

    看到官方教程中HTTP小节之前还在向同事夸angularjs的教程做的厚道 没有什么坑 结果到http就出现了 发现了两处错误 度娘各种搜索没有发现相关的帖子 于是记录下来 希望能被高效收录 以解广大IT民工之困扰 getHero id n
  • stream流常用

    从一个List中获得每个object的对象的id组成一个list List
  • 卓越性能代码_「Win」被隐藏起来的卓越性能模式,为何不想让人发现?

    前言 众所周知 电脑电源管理中包含三大模式 分别是 节能模式 平衡模式 高性能模式 其对电脑的性能影响还是比较大的 但是今天所说的 卓越性能模式 应该很多人都没听说过 又是何方神圣 其为何要隐藏起来不想被人发现 如何开启 卓越性能 模式 右
  • LLaMA微调记录

    本文基于开源代码https github com Lightning AI lit llama tree main执行微调 其他参考链接 Accelerating LLaMA with Fabric A Comprehensive Guid
  • 实验二:使用KMP算法实现字符串的匹配

    1 实验目的 熟练的掌握数据结构中串这种数据类型 学会使用相较于朴素的模式识别算法更加先进的KMP算法进行识别和匹配 同时 在数据结构试验之中熟悉和了解串的性质和使用方法 2 实验要求 输入 通过命令行参数输入原字符串和模式字符串 输出 1