字符串匹配算法(BF、KMP)

news2025/2/24 0:55:54

目录

1、暴力匹配(BF)算法

2、KMP算法


1、暴力匹配(BF)算法

BF算法,即暴力(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T 的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和 T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。

假定我们给出字符串 ”ababcabcdabcde”作为主串, 然后给出子串: ”abcd”,现在我们需要查找子串是否在主串中 出现,出现返回主串中的第一个匹配的下标,失败返回-1 ;

只要在匹配的过程当中,匹配失败,那么 i回退到刚刚位置的下一个,j 回退到0下标重新开始。

 代码:

#include <stdio.h>
#include <string.h>
#include <assert.h>
//str 主串
//sub 子串
//如果找到了,返回子串在主串中的下标,否则,返回-1
int BF(char* str, char* sub)
{
	assert(str && sub);
	if (str == NULL && sub == NULL)
		return -1;

	int lenStr = strlen(str);
	int lenSub = strlen(sub);

	int i = 0;//主串开始的下标
	int j = 0;//子串开始的下标
	while (i < lenStr && j < lenSub)
	{
		if (str[i] == sub[j])
		{
			i++;
			j++;
		}
		else
		{
			i = i - j + 1;
			j = 0;
			
		}
	}
	//如果子串走到空,代表找到了
	if (j >= lenSub)
	{
		return i - j;
	}
	//如果主串走到空,证明主串里没有子串
	return -1;
}
int main()
{
	printf("%d\n", BF("ababcabcdabcde", "abcd"));//5
	printf("%d\n", BF("ababcabcdabcde", "abcde"));//9
	printf("%d\n", BF("ababcabcdabcde", "abcdef"));//-1
	return 0;
}

2、KMP算法

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫 里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次 数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n) 。

区别:KMP 和 BF 唯一不一样的地方在,我主串的 i 并不会回退,并且 j 也不会移动到 0 号位置 

KMP 的精髓就是 next 数组:也就是用 next[j] = k;来表示,不同的 j 来对应一个 K 值, 这个 K 就是你将来要移动的 j 要移动的位置。 

1、规则:找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标 0 字符开始,另一个以 j-1下标字符结尾。

2、不管什么数据 next[0] = -1;next[1] = 0;在这里,我们以下标来开始,而说到的第几个第几个是从 1 开始(next[0]也可以是0,只需要在写代码的时候控制好就可以了)。

求next数组:找到两个相等的字符串,第一个串必须以0下标的字母开头,第二个串必须以下标为j-1(j为目标字符)的字母结尾,这串相等的字符串的长度就是目标字母next数组的值。

例1:对于”ababcabcdabcde”, 求其的 next 数组?

 例2:”abcabcabcabcdabcde”,求其的 next 数组?

 对此,我们对next数组是什么也有了一定的了解。根据观察,我们发现当p[i]==p[k]时(p为字符串),next数组会递增,那么如何证明呢?

接下来的问题就是,已知next[i] = k;怎么求next[i+1] = ?

如果我们能够通过 next[i]的值,通过一系列转换得到 next[i+1]得值,那么我们就能够实现这部分。

 代码:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

void GetNext(int* next, const char* sub)
{
	int lensub = strlen(sub);
	next[0] = -1;
	next[1] = 0;
	int i = 2;//下一项
	int k = 0;//前一项的K
	while (i < lensub)//next数组还没有遍历完
	{
		if ((k == -1) || sub[k] == sub[i - 1])//
		{
			next[i] = k + 1;
			i++;
			k++;//k = k+1下一个K的值新的K值
		}
		else
		{
			k = next[k];
		}
	}
}
int KMP(const char* s, const char* sub, int pos)
{
	int i = pos;
	int j = 0;
	int lens = strlen(s);
	int lensub = strlen(sub);
	int* next = (int*)malloc(lensub * sizeof(int));//和子串一样长
	assert(next != NULL);
	GetNext(next, sub);
	while (i < lens && j < lensub)
	{
		if ((j == -1) || (s[i] == sub[j]))
		{
			i++;
			j++;
		}
		else
		{
			j = next [j];
		}
	}
	free(next);
	if (j >= lensub)
	{
		return i - j;
	}
	else
	{
		return -1;
	}
}
int main()
{
	char* str = "ababcabcdabcde";
	char* sub = "abcd";
	printf("%d\n", KMP(str, sub, 0));
	return 0;
}

next数组的优化 

next 数组的优化,即如何得到 nextval 数组:有如下串: aaaaaaaab,他的 next 数组是-1,0,1,2,3,4,5,6,7. 而修正后的数组 nextval 是: -1, -1, -1, -1, -1, -1, -1, -1, 7。为什么出现修正后的数组,假设在 5 号处失败了,那退一步还是 a,还是相等,接着退还是 a。

 

nextval数组的求法很简单,如果当前回退的位置,正好是和当前字符一样,那么就写那个字符的nextval值。不一样就写自己的。

如上面例子中:4的下标字符a,应该回退到下标为1位置,下标为1位置不是a.所以,nextval值,就是当前next值。
比如5的下标字符b,应该回退到下标为1位置,下标为1位置也是b.那么此时的nextval值,就是1下标b的nextval值。

优化后得到next数组代码:

void GetNext(int* next, const char* sub)
{
	int lensub = strlen(sub);
	next[0] = -1;
	next[1] = 0;
	int i = 2;//下一项
	int k = 0;//前一项的K
	while (i < lensub)
	{
		if ((k == -1) || sub[k] == sub[i - 1])
		{
			
			if (sub[i] == sub[k])//当两个字符相同时,就跳过
				next[i] = next[k];
			else
				next[i] = k + 1;
			i++;
			k++;
		}
		else
		{
			k = next[k];
		}
	}
}

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

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

相关文章

【树莓派不吃灰】配置samba,文件夹目录配置在闲置U盘,实现局域网文件共享

目录1. 前言2. 安装 Samba2.1 安装samba 和 samba-common-bin2.2 配置/etc/samba/smb.conf文件2.3 配置登录账号和密码2.4 重启 samba 服务2.5 回到windows&#xff0c;就可以在网络当中发现共享的文件夹3. 在Windows上挂载smb的共享目录3.1 打开windows的smb功能3.2 添加网络映…

Java --- springMVC实现RESTFul案例

一、使用springMVC实现RESTFul小案例 1.1、项目目录图&#xff1a; 1.2、代码实现&#xff1a; pom.xml文件&#xff1a; <packaging>war</packaging><!--添加依赖--><dependencies><!--SpringMVC--><dependency><groupId>org.spr…

黑马C++ 03 提高4 —— STL常用容器_string容器/vector容器/deque容器

文章目录一、string容器1. string基本概念2. string构造函数3. string赋值操作4. string字符串拼接5. string查找和替换6. string字符串比较7. string字符存取8. string字符串的插入和删除9. string子串二、vector容器(尾插尾删)1. vector基本概念2. vector构造函数3. vector赋…

【目标检测】基于yolov3的血细胞检测(无bug教程+附代码+数据集)

多的不说,少的不唠,先看检测效果图: 共检测三类:红细胞RBC、白细胞WBC、血小板Platelets Hello,大家好,我是augustqi。今天给大家带来的保姆级教程是:基于yolov3的血细胞检测(无bug教程+附代码+数据集) 1.项目背景 在上一期的教程中,我们基于yolov3训练了一个红细…

韩顺平linux(1-11小节)

运维工程师 服务器的规划、调试优化、日常监控、故障处理 物联网linux Linux主要指的是内核 ubuntu&#xff08;python偏爱&#xff09;&#xff0c;centos 发行版本 内核进行包装 1.4服务器领域 linux在服务器领域的应用是最强的。 linux免费、稳定、高效等特点在这里得到了很…

2019 Sichuan Province Programming Contest J. Jump on Axis

题目链接&#xff1a;https://codeforces.com/gym/102821/problem/J 题意&#xff1a;给你一个坐标k&#xff0c;每次从0开始走 每次有三个选择&#xff1a;选择1走一步&#xff0c;选择2走两步&#xff0c;选择3走三步 每次选第i个选择的时候&#xff0c;如果他没有被选过&…

MySQL是如何保证数据不丢失的

一.什么是两阶段提交 1.SQL语句&#xff08;update user set name‘李四’ where id3&#xff09;的执行流程是怎样的呢&#xff1f; 1.执行器先找引擎取 ID3这一行。ID 是主键&#xff0c;引擎直接用树搜索找到这一行。 2.如果 ID3 这一行所在的数据页本来就在内存中&#x…

力扣算法入门刷题

1、回文数 判断输入的整数是否是回文 我的一般思路&#xff1a; 将输入的整数转成字符串&#xff0c;再将这个字符串转成字符数组c&#xff0c;对字符数组进行遍历&#xff0c;如果第i个元素与第 c.length - i - 1 元素不相等&#xff0c;也就是通过比较首尾元素是否相同来判断…

自动化早已不是那个自动化了,谈一谈自动化测试现状和自我感受……

前言 从2017年6月开始接触自动化至今&#xff0c;已经有好几年了&#xff0c;从17年接触UI自动化&#xff08;unittestselenium&#xff09;到18年接触接口自动化&#xff08;unittestrequests&#xff09;再到18年自己编写自动化平台&#xff08;后台使用python的flask&#…

风、光、柴油机、蓄电池、电网交互微电网经济调度优化问题研究附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

爆破校园网的宽带

前提&#xff1a;学校的手机号前7位相同&#xff0c;宽带密码都是手机号后六位。仅供学习。 准备工作&#xff1a;电脑一台&#xff0c;把校园网的宽带水晶头插在电脑上&#xff0c; 步骤&#xff1a; winR输入Rasphone点击新建&#xff0c;宽带&#xff0c;输入宽带名称&am…

Vue复刻华为官网 (一)

1 分析 根据华为网页的布局&#xff0c;我们大体上可以将其划分为7个盒子&#xff0c;如下&#xff0c;由于写一个这样的网页再加上部分动态效果&#xff0c;需要的时间很长&#xff0c;本篇博客只记录了div1、div2、div3的静态效果轮播图的实现。 2 顶部盒子的实现 想要实现的…

【C++AVL树】4种旋转详讲

目录 引子&#xff1a;AVL树是因为什么出现的&#xff1f; 1.AVl树的的特性 2.AVl树的框架 3.AVL树的插入 3.1四种旋转&#xff08;左单旋、右单旋、左右双旋、右左双旋&#xff09; 3.1.1左单旋 3.1.2右单旋 3.1.3左右双旋 3.1.4右左双旋 总结 引子&#xff1a;AVL树是因…

【单片机】单片机的核心思想

&#x1f4ac;推荐一款模拟面试、刷题神器 、从基础到大厂面试题&#xff1a;&#x1f449;点击跳转刷题网站进行注册学习 目录 一、单片机的核心思想 二、单片机核心图 三、上拉电路及应用 排阻的优势 四、单片机的输入输出模式 1、接收外部电压信号 2、向外输出电压信…

0089 时间复杂度,冒泡排序

/* * 排序也称排序算法&#xff08;Sort Algorithm&#xff09; * 排序是将一组数据&#xff0c;依指定的顺序进行排列的过程。 * * 排序分类 * 1.内部排序&#xff1a;将需要处理的所有数据都加载到内存存储器中进行排序&#xff08;使用内存&#xff09; * 插…

彻底搞懂WeakMap和Map

一 、Map Map是一种叫做字典的数据结构&#xff0c;Map 对象保存不重复键值对&#xff0c;并且能够记住键的原始插入顺序 Map的属性和方法* 属性&#xff1a; size&#xff1a; 返回所包含的键值对长度* 操作方法&#xff1a;* set(key,val): 添加新键值对* get(key): 通过传…

Linux--信号signal、父子进程、SIGCHLD信号相关命令

目录 1.概念&#xff1a; 2.信号的存储位置&#xff1a; 3.常见的信号的值以及对应的功能说明&#xff1a; 4.信号的值在系统源码中的定义&#xff1a; 5.响应方式&#xff1a; 6.改变信号的相应方式&#xff1a; (1)设置信号的响应方式: (2)默认:SIG_DFL;忽略:SIG_IGN…

Android Studio 新版本 Logcat 的使用

前言 最近&#xff0c;Android Studio 自动更新了自带的 Logcat 工具&#xff0c;整体外观和使用方法变得和之前完全不同了。一开始我以为是自己按到什么不该按的按钮&#xff0c;把 Logcat 弄坏了&#xff0c;后来才知道是版本更新导致的。新版本的 Logcat 用命令来过滤信息&…

jmeter变量函数以及抓包用法

抓包 代理服务器&#xff1a; 自己启动一个代理服务器 本地&#xff0c;要使用代理服务器的ip和端口&#xff0c;使用自己启动的代理服务器 操作步骤 添加线程组测试计划 > 非测试元件 > http代理服务器一定要修改 修改为** 测试计划>线程 ip就是你自己电脑的ip&…

Activity

Activity生命周期图 官网的 Activity 的生命周期图&#xff1a; 在官方文档中给出了说明&#xff0c;不允许在 onPause() 方法中执行耗时操作&#xff0c;因为这会影响到新 Activity 的启动。 常见情况下Activity生命周期的回调 &#xff08;A 与 B 表示不同的 Activity &a…