字符串的模式匹配

news2024/9/22 5:55:31

字符串的模式匹配

  • 引言
  • 应用
  • 方法一 暴力匹配算法 (C语言实现)
    • 程序实现
    • 暴力算法思想
    • 暴力算法的时间复杂度
  • 方法二 KMP 算法
    • 程序实现
    • KMP 算法思想
    • KMP 算法的时间复杂度
    • 暴力匹配算法 vs KMP 算法
    • next 数组的训练
  • KMP 算法的优化
    • next 数组 转换成 nextval 数组的思想

引言

在我们日常生活中,需要从一个网站上搜索关键字,实际上网站会帮我们从数据库中寻找与这些关键字相匹配的句子或者一段话。

1-

应用

对于上面的例子,一个完整的句子我们可以称其主串,输入的关键字我们可以称其模式串,在主串中匹配相同的关键字,我们可以说是在主串中找与模式串相同的子串。

所以,这就是本篇博客所讨论的模式匹配:在主串中,寻找与模式串相同的子串,并返回子串的起始位置。举两个简单的例子,情况如下:

1-1

方法一 暴力匹配算法 (C语言实现)

程序实现

#include <stdio.h>

int find_substring(char arr1[], char arr2[], int len1, int len2) {

	int i = 1;
	int j = 1;

	while (i <= len1 && j <= len2) {
		if (arr1[i - 1] == arr2[j - 1]) {
			i++;
			j++;
		}else {
			i = i - j + 2;
			j = 1;
		}
	}

	// 跳出循环后,如果此刻的位置,j > len2,那么就是匹配成功
	if (j > len2) {
		return i - len2;
	}

	// 否则就是未找到模式串
	return -1;

}

int main() {

	char arr1[] = "abaxzabcax"; // 主串
	char arr2[] = "abc"; // 模式串

	int len1 = 10;
	int len2 = 3;

	int ret = find_substring(arr1, arr2, len1, len2);
	printf("%d\n", ret);

	return 0;
}

暴力算法思想

约定用 i 遍历主串序列,用 j 遍历模式串序列。

在主串中,从左往右扫描,每次以模式串的数量单位 (这里三个单位) 进行扫描,在扫描的过程中,拿每个字符和模式串对比。

1-

暴力算法的时间复杂度

最优的时间复杂度:O(1)
最坏的时间复杂度:O(nm)

讨论最坏的情况:主串长度序列为 n,模式串长度序列为 m.

一般情况,主串长度大于模式串,那么我们在主串中寻找子串时,最坏情况下每次都要匹配 m 个元素,最多匹配 n-m+1 次,才能彻底匹配完。即 O( m * [n-m+1] ) ≈ O(nm).

1-3

方法二 KMP 算法

程序实现

#include <stdio.h>

int KMP_find_substring(char* arr1, char* arr2, int len1, int len2, int* next) {
	int i = 1;
	int j = 1;
	while(i <= len1 && j <= len2){
		if (j == 0 || arr1[i - 1] == arr2[j - 1]) {
			i++;
			j++;
		}else {
			j = next[j];
		}
	}

	// 跳出循环后,如果此刻的位置,j > len2,那么就是匹配成功
	if (j > len2) {
		return i - len2;
	}

	// 否则就是未找到模式串
	return -1;
}

int main() {

	char arr1[] = "abaacaabcabaabc"; // 主串
	char arr2[] = "abaabc";	// 模式串
	int next[] = { 0,0,1,1,2,2,3 }; 
	
	// 数组下标:		0 1 2 3 4 5 6
	// 重置后的 j:	/ 0 1 1 2 2 3

	int len1 = 15;
	int len2 = 6;

	int ret = KMP_find_substring(arr1, arr2, len1, len2, next);
	printf("%d\n", ret);

	return 0;
}

KMP 算法思想

约定用 i 遍历主串序列,用 j 遍历模式串序列。

① 根据模式串设计 next 数组,用来重置下一次 j 的位置。
② 根据 next 数组中的 j,来间断遍历主串序列,在使用 i 遍历主串序列时,整个过程不回溯。

例如:对于模式串为 " abaabc" 的序列,求 next 数组。

1-4

计算后的 next 数组:

1-5

接下来,根据 next 数组来间断地遍历主串序列。

1-6

KMP 算法的时间复杂度

最优的时间复杂度:O(1)
最坏的时间复杂度:O(n + m)

讨论最坏的情况:主串长度序列为 n,模式串长度序列为 m.

根据模式串长度序列为 m ,推出 next 数组长度也为 m. 所以在最坏的情况下,一定会遍历 next 数组,时间复杂度为 O(m). 此外,遍历主串的过程中,当字符匹配失败时不产生回溯,即 O(n).

综上所述,KMP 算法的最坏复杂度为 O(n + m)

暴力匹配算法 vs KMP 算法

我们来看一个极端的情况:

// 主串
a a a a a a a a a b

// 模式串
a a a a b

约定用 i 遍历主串,用 j 遍历模式串。

如果用暴力算法,每次匹配到模式串最后一个元素时,i 都要回溯;但使用 KMP 算法时,i 便不会产生回溯,它只会不断地主串右端走。也就是说,暴力算法在每一次失配时,都会重新遍历;而 KMP 算法抓住了模式串的特点,从而省去了回溯的过程,即主串指针 i 只会越来越大。

综上所述,如果我们使用 KMP 算法时,我们需要清楚一件事情,即每次遍历主串的算法关键在于模式串的序列是什么样的,不同的模式串,所对应的 next 数组不同,则每次匹配的方式也不同。

next 数组的训练

模式串 " google "

数组下标:	0 1 2 3 4 5 6
模式串序列:  \ g o o g l e
新的 j:		\ 0 1 1 1 2 1

模式串 " aaab "

数组下标:	0 1 2 3 4 5 
模式串序列:  \ a a a a b
新的 j:		\ 0 1 2 3 4

KMP 算法的优化

我们先来看一个普通的 KMP 算法的使用例子。

1-

观察上面的主串与模式串的匹配,可以发现,中间三步较为多余。因为第一次匹配的时候,主串中 i=4 的位置与模式串中 j=4 的位置失配,接着就重置 j=3,然而在模式串中 j=3 的位置也是 “字符a”,所以,i=4 与 j=3 在判定时也必然失配。 同样的道理,模式串 j=1、j=2 也是 “字符a”,所以上面的中间三步必然会匹配失败。

综上所述,我们对于 next 数组的元素进行改变,将其变成一个 nextval 数组,而同时不改变 KMP 算法的代码,如下:

1-

可以发现,在 KMP 算法中,使用了 nextval 数组,将会使得匹配次数减少。

next 数组 转换成 nextval 数组的思想

1-

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

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

相关文章

第12章 角色页的修改、添加

1 定义src\components\Users\EditRole.vue <template> <el-dialog width"30%"> <!-- <span>{{propParent}}</span> --> <template #header> <div class"my-header"> <h1 style"margin: 0px; padding: …

快速指南 :ESP-IDF 自定义以太网 PHY 驱动程序

“我想用我最喜欢的芯片开始新的产品设计&#xff0c;但它断货了&#xff01;哦&#xff0c;不&#xff01;我必须设计一个新的 PCB&#xff0c;并重新开发驱动程序&#xff01;”如今&#xff0c;每个设计师都非常清楚这种感觉…好消息是&#xff0c;至少在 ESP-IDF 以太网 PH…

[C语言]浮点型在内存中的存储

在上一篇文章&#xff0c;我们讲述了整型在内存中的存储&#xff0c;这篇文章我们就一起来看一下“浮点型在内存中的存储” 回顾&#xff1a;整型在内存中的存储[C语言]和我一起来认识“整型在内存中的存储”_HY_PIGIE的博客-CSDN博客 目录 1.浮点数家族 2.整型和浮点型的存储…

教你从零开始搭建自己的魔兽世界服务器

首先需要一份 WOW 的程序底包:1底包使用方法: 解压后,放到 d: 目录即可, 如下图 &#xff08;最好是D盘下 因为有很多东西都是D:/连接的 &#xff09;2运行http-mysql/下的文件INIT.CM_重命名为INIT.CMD 运行3设置登录器下载 &#xff0c;在http-mysql/htdocs下创建DOWNLOAD文件…

再次改进MBR(从磁盘读入Loader加载器)

文章目录前言前置知识代码说明实验操作前言 本博客记录《操作系统真象还原》第二章第2个实验操作~ 实验环境&#xff1a;ubuntu18.04VMware &#xff0c; Bochs下载安装 实验内容&#xff1a;从磁盘读入Loader加载器 实验思路&#xff1a; MBR 受到512字节大小的限制&#…

kernel pwn gdb调试

前言 对于Linux的二进制程序&#xff0c;gdb调试是十分重要的&#xff0c;可以清楚的了解程序是如何运行的&#xff0c;这里单独拉一篇记录我在kernel pwn中遇到的一些调试 GDB选择 在三大件pwndbg,gef,peda中&#xff0c;用了一圈下来感觉gef和pwndbg都挺好 gdb安装 简单…

PaddleNLP教程文档

文章目录一、快速开始1.1 安装PaddleNLP并 加载数据集1.2 数据预处理1.3 加载预训练模型1.4 设置评价指标和训练策略1.5 模型训练与评估1.6 模型预测二、数据处理2.1 整体介绍2.2 加载内置数据集2.3 自定义数据集2.3.1 从本地文件创建数据集2.3.2 paddle.io.Dataset/IterableDa…

OpenShift Security - 用 RHACS 为应用自动生成 NetworkPolicy

《OpenShift / RHEL / DevSecOps / Ansible 汇总目录》 说明&#xff1a;本文已经在 OpenShift 4.12 RHACS 3.73.1 环境中验证 文章目录什么是 NP-Guard用 NP-Guard 自动生成 NetworkPolicy参考什么是 NP-Guard NP-Guard 是 IBM 发起的一个开源项目&#xff0c;用来自动创建 …

WindowsTerminal 安装 oh-my-posh

文章目录1 前言2 安装过程3 Posh Themes 自定义主题参考1 前言 在Linux中&#xff0c;有非常好用的oh-my-zsh&#xff0c;最近使用WindowsTerminal时想想有没有和oh-my-zsh相同好用的插件呢&#xff0c;答案是&#xff1a;oh-my-posh 2 安装过程 进入最新版PowerShell&#…

干货 | 解决 App 自动化测试的常见痛点(弹框及首页启动加载完成判断处理)

1. 常见痛点App 自动化测试中有些常见痛点问题&#xff0c;如果框架不能很好的处理&#xff0c;就可能出现元素定位超时找不到的情况&#xff0c;自动化也就被打断终止了。很容易打消做自动化的热情&#xff0c;导致从入门到放弃。比如下面的两个问题&#xff1a;一是 App 启动…

【代码题】链表面试题

目录 1.链表分割 2.相交链表 3.环形链表 4.环形链表 II 1.链表分割 点击进入该题 现有一链表的头指针 ListNode* pHead&#xff0c;给一定值x&#xff0c;编写一段代码将所有小于x的结点排在其余结点之前&#xff0c;且不能改变原来的数据顺序&#xff0c;返回重新排列后的…

国内注册Steam账号的快捷方法

本文介绍在国内注册Steam账号的快速、简便方法。 目前&#xff0c;在国内注册新的Steam账号变得越来越麻烦&#xff1b;尤其在最近&#xff0c;无论是无休止的谷歌人机身份验证&#xff0c;还是无法收到的验证邮件&#xff0c;都使得新建一个Steam账号与原来相比变得更加困难。…

[Linux_]make/Makefile

[Linux_]make/Makefile 心有所向&#xff0c;日复一日&#xff0c;必有精进专栏&#xff1a;《Linux_》作者&#xff1a;沂沐沐目录 [Linux]make/Makefile 前言 一、Mikefile 二、如何写Mikefile文件 三、原理 四、项目清理 报错&#xff1a;missing separator 前言 一个工…

Linux 命令

最基础的命令 1.我是谁 我用什么账号登录 whoami 2.我在那 在那个目录下 pwd 3.环顾四周 1.ll展示详细信息 2.ls 展示文件名称 4.cd 想去那 改变目录 cd 回家 cd ./ 定位到当前目录 cd../ 上级目录 cd../../上两级目录。 5.切换用户 su 从普通用户切换到root用户的…

C#应用程序配置文件(XML序列化) - 开源研究系列文章

上次写了一个C#线程池及管理器的博文( C#开发的线程池和管理器 - 开源研究系列文章 )&#xff0c;收到了不小的浏览量&#xff0c;在此感谢各位网友的支持。这次将另一个功能放出来单独讲解&#xff1a;C#应用程序的配置文件&#xff0c;使用的是XML文件保存程序的配置信息&…

数据结构(栈、队列、链表)

文章目录前言数据结构(栈、队列、链表)1、栈2、队列3、链表3.1、单向链表结构3.2、双向链表结构前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&…

多边形点集排序

一、问题描述已知多边形点集P {P1, P2, ... , PN}&#xff0c;其排列顺序是杂乱&#xff0c;依次连接这N个点&#xff0c;无法形成确定的多边形&#xff0c;需要对点集P进行排序后&#xff0c;再绘制多边形。二、排序规则点集排序过程中&#xff0c;关键在于如何定义点的大小关…

STC32G 时钟系统

文章目录时钟系统代码配置总结时钟系统 系统时钟有4个时钟源可供选择&#xff1a; 内部高精度IRC内部32KHzIRC&#xff08;精度较低&#xff09;外部晶振内部PLL输出时钟 主要关心的是两个指标&#xff1a;SYSclk和HSCLK SYSclk是系统的时钟&#xff0c;决定了指令执行速度…

Android 深入系统完全讲解(三)

系统调用 操作系统提供了一些方法&#xff0c;让用户层可以调用&#xff0c;而为了安全起见&#xff0c;这些方法调用&#xff0c;都是在内核空间。于是&#xff0c;用户调用的时候&#xff0c;就会有个动作&#xff0c;叫做陷入内核。 当用户调用系统方法的时候&#xff0c;系…

【k8s-device plugin】如何编写 k8s device plugin

参考 Device Plugin 入门笔记&#xff08;一&#xff09; Device Plugin 入门笔记&#xff08;二&#xff09; 从零开始入门 K8s&#xff1a;GPU 管理和 Device Plugin 工作机制 Kubernetes开发知识–device-plugin的实现 https://github.com/oceanweave/cola-device-plugi…