字符串匹配

article/2025/10/4 1:03:05

字符串匹配

1.朴素的串匹配算法(暴力解法)

1.1 分析


设t是目标串(母串),p是模式串(待匹配串),i , j 分别指向 模式串 和 目标串,m、n分别是模式串p和目标串t的长度。

  • 从目标串的第0个字符,挨个进行比较,遇到不相等的字符就停止。
  • 模式串与目标串的下一个字符进行比较,重复上一个步骤。
  • 一个一个字符遍历目标串直到找到为止。

1.2 Python实现:

def match(t:str, p:str):  '''  t:目标串(母串)  p:模式串(要匹配的字符串)  返回与模式串在目标串中第一次出现时的下标''' m, n = len(p), len(t) # m 和 n分别是字符串 p 和 t的长度  i, j = 0, 0  while i < m and j < n:  if p[i] == t[j]:  i, j = i+1, j+1  else:  i, j = 0, j-i+1 # j-i得到当前j的下标,+1指向目标串的下一个元素  if i == m:  return j-i             # 返回此时母串中j的下标  return -1res = match("ababcabcacbab", "abcac")  
print(res)

程序运行结果是5;

1.3 时间复杂度分析

暴力匹配最坏的情况下,每一次都进行比较,最后一趟才匹配上,共n-m+1趟,每次模式串都进行了匹配为m次,故这个算法的时间复杂度为:O(m x n)

朴素算法(暴力匹配)的缺点:

  • 把每次字符都看做完全独立的操作,没有利用字符串本身的特点,字符串只有有穷多个字符。
  • 没有利用前面已经比较的字符串得到的信息。

匹配字符串的特点:

  • 模式串不太长
  • 目标串的每一个字符来源于一个又穷集合,不是无穷多种取值
  • 每个串有固定的长度

2.无回溯串匹配算法(KMP算法)

KMP算法是由3个外国人最先发现的,并以他们的名字首字母命名,该算法是一个高效的串匹配算法,该算法比较难理解,但是时间复杂度大大降低。该算法主要优化了朴素算法里把模式串里的字符看做单独随机字符的做法。具体如下:

  • 每一次比较之后,找到不同的元素
  • 然后通过next数组找到模式串下一次匹配的字符下标
  • 构造next数组

2.1 KMP算法主函数

例:使用KMP算法把下图中的模式串在目标串进行进行查找,返回第一次出现时的下标:

def match_KMP(t, p, gen_pnext):  """:arg  t : 目标串  p : 模式串  next : 模式串的next数组  """ i, j = 0, 0  n, m = len(t), len(p)   # m 、 n 分别是模式串 和 目标串的长度  while i < m and j < n:  if i == -1 or t[j] == p[i]:  i, j = i+1, j+1  else:  i = gen_pnext[i]  if i == m:              # 找到匹配的子字符串,返回其下标  return j-i  return -1

2.2 next数组

首先我们介绍一下前缀和后缀的概念:
给出一个字符串 abcacab 该字符串相等的最长的前缀和后缀是ab
练习:
1.求字符串 abbcabb,该字符串相等的前后缀为abb。
2.求字符串acgcca ,该字符串相等的前后缀为 a。
通过找到相等的前后缀,我们主要用来在KMP匹配一次之后,目标串和模式串遇到不同的字符时,在该位置目标串下次匹配的模式串字符的位置。如上图所示:目标串的a字符和模式串的c字符不一致,这时候需要构造next数组找到下次模式串匹配的字符位置a,这里的计算方法是:在模式串的不同字符的前的子字符串里找到相等的最大的前后缀的下一个字符的下标作为下次模式串匹配的位置。这里 ab 字符,没有相等的前后缀,返回下标0,对应的字符是a。

第二次匹配时:遇到目标串里的字符 b 和 模式串里的 c 不一致,我们找 子字符串:abca 里 最长相等前后缀a,则取下标1对应的 b 字符与第一次匹配失败时的 目标串的 ‘b’ 字符进行匹配。

def gen_pnext(p):  i, k, m = 0, -1, len(p)  pnext = [-1] * m  while i < m-1:  if p[i] == p[k] or k == -1:  i, k = i+1, k+1  pnext[i] = k  else:  k = pnext[k]  return pnext

这里的 gen_pnext() 函数就是我们上面解释的生成 next 数组的函数。下面稍微容易理解一点。

def p_next(patttern:str):  """:arg  pattern:传入的是待匹配字符串  """ i, k = 0, -1 # i 指向 pattern 字符串, k 初值为 -1 pnext = [-1] * len(patttern) # 生成一个长度和 pattern 一致的数组,初始值都为-1  while i < len(patttern)-1:  if k == -1:  i, k = i + 1, k + 1  pnext[i] = k  elif patttern[k] == patttern[i]:  i, k = i + 1, k + 1  pnext[i] = k  else:  k = pnext[k]  # 不相等 k 的值就会一直指向 pnext[k] return pnext

上面的求next的函数很不好理解,可以结合图片理解。

2.3 时间复杂度

一次KMP算法的完整执行包括构造 pnext 表 和 实际匹配,设模式串和目标串长度分别为 m 和 n, KMP算法的时间复杂度是 O(m+n)。多数情况下 m<<n,可以认为这个算法的时间复杂度是 O(n)。

码字不易,感觉对你有帮助可以加个关注哈。


http://chatgpt.dhexx.cn/article/txl01EMM.shtml

相关文章

Photoshop怎么给图片添加简介信息或者版权信息

转自&#xff1a;Photoshop怎么给摄影图片添加作者名字版权等信息? 有时我们点开一张图片的详细信息中可能可以看到各种属性信息&#xff0c;比如作者&#xff0c;时间&#xff0c;关键字&#xff0c;图片信息描述等属性&#xff0c;但是我们自己的拍摄的或者从别的地方获取的…

2022年中国版权保护中心计算机软件著作权登记最全申请步骤流程

一、前言二、实名认证1. 用户注册2. 实名认证 三、办理步骤1. 办理流程2. 填写申请表3. 提交申请文件4. 登记机构受理申请5. 审查6. 获得登记证书 四、登记申请所需文件1. 软件著作权登记申请表2. 软件&#xff08;程序、文档&#xff09;的鉴别材料3. 有关证明文件 五、申请表…

IDEA设置版权信息

File→Settings或者CtrlS快捷键。 Editor下面有个Copyright→Copyright Profiles 点击加号&#xff0c;然后输入名称。 然后修改成自己的信息&#xff1a; 其中第一个年份是本文件新建日期&#xff0c;后面的是最后一次修改年份。 中文版本&#xff1a; 版权所有(c) Jack魏 …

版权和版本信息

版权和版本信息的主要内容有&#xff1a; &#xff08;1&#xff09;版权信息&#xff1b; &#xff08;2&#xff09;文件名称、简要描述、创建日期和作者&#xff1b; &#xff08;3&#xff09;当前版本信息和说明&#xff1b; &#xff08;4&#xff09;历史版本信息和…

版权和商标权有什么关系?版权和商标的区别在哪里?

版权和商标权存在着一定的关系&#xff0c;版权和商标又有着很多区别&#xff0c;具体的关系和区别是怎样的&#xff0c;大家都知道吗&#xff1f;今天企多多就带大家来了解&#xff01; 版权和商标权的关系 版权和商标权的关系主要有以下三点&#xff1a; 1、关联性&#xf…

版权 | 收藏!哪些作品可以登记版权?

创业创新中&#xff0c;无论是公司LOGO还是IP形象或者产品手册&#xff0c;都凝结着无数的心血。当下互联网和科技的发展&#xff0c;让抄袭变得前所未有的容易&#xff0c;尤其在美术作品、文字作品和影视作品领域。如何有效地保护自己的智力成果呢&#xff1f;先从了解这些开…

Pyinstaller加入版本和版权信息

目录 参考链接 前言 一. 获取版本信息 1. 拖过来个有版本和版权信息的exe文件 2. 放置一个txt文件 我们接着放置一个txt文件叫file_version_info.txt。这名字不能改&#xff0c;Pyinstaller自动就把版权信息放在这里。 3.开始获取 二. 修改 三. 打包 参考链接 pyinsta…

版权信息的生成方法

网页底部添加网站的版权信息&#xff0c;将版权信息封装到JavaBean中&#xff0c;可重复利用 新建JavaBean类 public class StringUtil3 {private String copyrightStr"xxxxxxxxxxx xxxxxxxxxxxxx--xxxxxxxxxxxxxxxxxxxx-xxx";public String getCopyrightStr() {ret…

C++虚函数表

一、背景知识&#xff08;一些基本概念&#xff09; 虚函数&#xff08;Virtual Function&#xff09;&#xff1a;在基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数。 纯虚函数&#xff08;Pure Virtual Function&#xff09;&#xff1a;基类中没有实现体…

虚函数和虚函数表

多态是由虚函数实现的&#xff0c;而虚函数主要是通过虚函数表&#xff08;V-Table&#xff09;来实现的。 如果一个类中包含虚函数&#xff08;virtual修饰的函数&#xff09;&#xff0c;那么这个类就会包含一张虚函数表&#xff0c;虚函数表存储的每一项是一个虚函数的地址…

C++ 虚函数表 vfptr

前言 大家都应该知道C的精髓是虚函数吧? 虚函数带来的好处就是: 可以定义一个基类的指针, 其指向一个继承类, 当通过基类的指针去调用函数时, 可以在运行时决定该调用基类的函数还是继承类的函数. 虚函数是实现多态(动态绑定)/接口函数的基础. 可以说: 没有虚函数, C将变得一…

c++ 虚函数及虚函数表

多态”的关键在于通过基类指针或引用调用一个虚函数时&#xff0c;编译时不确定到底调用的是基类还是派生类的函数&#xff0c;运行时才确定。 #include <iostream> using namespace std; class A { public:int i;virtual void func() {}virtual void func2() {} }; cla…

虚函数表结构

虚函数表 所谓虚函数表就是存放着当前类中所有虚函数地址的表。在实例化一个具有虚函数的类时&#xff0c;这个表也被分配到这个实例对象的内存中&#xff0c;通过虚函数表可以指明所要调用的函数的位置。在C编译器中虚函数表的地址存放在对象的最前面&#xff0c;这是为了即使…

关于虚函数与虚函数表

首先&#xff0c;我们知道&#xff0c;C的动态多态是基于虚函数实现的 。 C能够在运行时确定调用的函数是因为引入了虚函数&#xff0c;在类中引入虚函数后,在程序编译期间就会创建虚函数表&#xff0c;表中每一项数据都是虚函数的入口地址。 然而&#xff0c;怎么才能访问到虚…

C++中的虚函数表

引言&#xff1a; 多态对于C这种面向对象的语言来讲&#xff0c;其重要性是不言而喻的&#xff0c;用了足足半天的时间来把我所理解的多态表达出来&#xff0c;其中还有很多细节需要以后补充。&#xff08;一个字一个字写&#xff0c;还要画图&#xff0c;太累了&#xff09; …

虚函数原理与虚函数表

目录 一、 虚函数 二、虚函数原理与虚函数表 一、 虚函数 虚函数&#xff1a; 使用 virtual 关键字声明的函数&#xff0c;是动态多态实现的基础。 非类的成员函数不能定义为虚函数。 类的静态成员函数不能定义为虚函数。 构造函数不能定义为虚函数&#xff0c;但可以将析构函…

c++虚函数和虚函数表

什么是虚函数? 用virtual 修饰的成员函数叫虚函数 没有虚构造函数 不写虚函数&#xff0c;没有默认的虚函数 虚函数对于类的影响&#xff1a;增加一个指针的内存 虚函数的存储&#xff1a;虚函数表(了解内容&#xff1a;就是一个指针存储所有虚函数的首地址[函数指…

虚函数表存储位置

前言 先说结论&#xff1a;虚函数表存储在只读数据段&#xff08;.rodata&#xff09;、虚函数存储在代码段&#xff08;.text&#xff09;、虚表指针的存储的位置与对象存储的位置相同&#xff0c;可能在栈、也可能在堆或数据段等。 相信大家知道虚表指针和虚函数存储的位置…

C++虚函数表详解

C的虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中&#xff0c;主要是一个类的虚函数的地址表&#xff0c;这张表解决了继承、覆盖(override)的问题&#xff0c;保证其能真实的反应实际的函数。这样&#xff0c;在有虚函数的类…

【虚函数指针 虚函数表】

文章目录 虚函数指针和虚函数表1.虚函数的含义2.虚函数的作用3.虚函数的实现原理 多态的实现原理普通类当类中存在虚函数子类继承父类不重写虚函数子类继承父类重写虚函数 1.虚函数表指针2.虚函数表 虚函数指针和虚函数表 1.虚函数的含义 只有用virtual声明类的成员函数&…