Boyer Moore 算法介绍

1. Boyer Moore 算法介绍

Boyer Moore 算法:简称为 BM 算法,是由它的两位发明者 Robert S. Boyer 和 J Strother Moore 的名字来命名的。BM 算法是他们在 1977 年提出的高效字符串搜索算法。在实际应用中,比 KMP 算法要快 3~5 倍。

  • BM 算法思想:对于给定文本串 T 与模式串 p,先对模式串 p 进行预处理。然后在匹配的过程中,当发现文本串 T 的某个字符与模式串 p 不匹配的时候,根据启发策略,能够直接尽可能地跳过一些无法匹配的情况,将模式串多向后滑动几位。

BM 算法的精髓在于使用了两种不同的启发策略来计算后移位数:「坏字符规则(The Bad Character Rule)」「好后缀规则(The Good Suffix Shift Rule)」

这两种启发策略的计算过程只与模式串 p 相关,而与文本串 T 无关。因此在对模式串 p 进行预处理时,可以预先生成「坏字符规则后移表」和「好后缀规则后移表」,然后在匹配的过程中,只需要比较一下两种策略下最大的后移位数进行后移即可。

同时,还需要注意一点。BM 算法在移动模式串的时候和常规匹配算法一样是从左到右进行,但是在进行比较的时候是从右到左,即基于后缀进行比较。

下面我们来讲解一下 BF 算法中的两种不同启发策略:「坏字符规则」和「好后缀规则」。

2. Boyer Moore 算法启发策略

2.1 坏字符规则

坏字符规则(The Bad Character Rule):当文本串 T 中某个字符跟模式串 p 的某个字符不匹配时,则称文本串 T 中这个失配字符为 「坏字符」,此时模式串 p 可以快速向右移动。

「坏字符规则」的移动位数分为两种情况:

  • 情况 1:坏字符出现在模式串 p
    • 这种情况下,可将模式串中最后一次出现的坏字符与文本串中的坏字符对齐,如下图所示。
    • 向右移动位数 = 坏字符在模式串中的失配位置 - 坏字符在模式串中最后一次出现的位置

  • 情况 2:坏字符没有出现在模式串 p
    • 这种情况下,可将模式串向右移动一位,如下图所示。
    • 向右移动位数 = 坏字符在模式串中的失配位置 + 1

2.2 好后缀规则

好后缀规则(The Good Suffix Shift Rule):当文本串 T 中某个字符跟模式串 p 的某个字符不匹配时,则称文本串 T 中已经匹配好的字符串为 「好后缀」,此时模式串 p 可以快速向右移动。

「好后缀规则」的移动方式分为三种情况:

  • 情况 1:模式串中有子串匹配上好后缀
    • 这种情况下,移动模式串,让该子串和好后缀对齐即可。如果超过一个子串匹配上好后缀,则选择最右侧的子串对齐,如下图所示。
    • 向右移动位数 = 好后缀的最后一个字符在模式串中的位置 - 匹配的子串最后一个字符出现的位置

  • 情况 2:模式串中无子串匹配上好后缀,但有最长前缀匹配好后缀的后缀
    • 这种情况下,我们需要在模式串的前缀中寻找一个最长前缀,该前缀等于好后缀的后缀。找到该前缀后,让该前缀和好后缀的后缀对齐。
    • 向右移动位数 = 好后缀的后缀的最后一个字符在模式串中的位置 - 最长前缀的最后一个字符出现的位置

  • 情况 3:模式串中无子串匹配上好后缀,也找不到前缀匹配
    • 可将模式串整个右移。
    • 向右移动位数 = 模式串的长度

3. Boyer Moore 算法匹配过程示例

下面我们根据 J Strother Moore 教授给出的例子,先来介绍一下 BF 算法的匹配过程,顺便加深对 「坏字符规则」「好后缀规则」 的理解。

  1. 假设文本串为 "HERE IS A SIMPLE EXAMPLE",模式串为 "EXAMPLE",如下图所示。

  1. 首先,令模式串与文本串的头部对齐,然后从模式串的尾部开始逐位比较,如下图所示。

可以看出来,'S''E' 不匹配。这时候,不匹配的字符 'S' 就被称为「坏字符(Bad Character)」,对应着模式串的第 6 位。并且 'S' 并不包含在模式串 "EXAMPLE" 中(相当于'S' 在模式串中最后一次出现的位置是 -1)。根据「坏字符规则」,可以把模式串直接向右移动 6 - (-1) = 7 位,即将文本串中 'S' 的后一位上。

  1. 将模式串向右移动 7 位。然后依然从模式串尾部开始比较,发现 'P''E' 不匹配,则 'P' 是坏字符,如下图所示。

但是 'P' 包含在模式串 "EXAMPLE" 中,'P' 这个坏字符在模式串中的失配位置是第 6 位,并且在模式串中最后一次出现的位置是 4(编号从 0 开始)。

  1. 根据「坏字符规则」,可以将模式串直接向右移动 6 - 4 = 2 位,将文本串的 'P' 和模式串中的 'P' 对齐,如下图所示。

  1. 我们继续从尾部开始逐位比较。先比较文本串的 'E' 和模式串的 'E',如下图所示。可以看出文本串的 'E' 和模式串的 'E' 匹配,则 "E" 为好后缀,"E" 在模式串中的位置为 6(编号从 0 开始)。

  1. 继续比较前面一位,即文本串的 'L' 和模式串的 'L',如下图所示。可以看出文本串的 'L' 和模式串的 'L' 匹配。则 "LE" 为好后缀,"LE" 在模式串中的位置为 6(编号从 0 开始)。

  1. 继续比较前面一位,即文本串中的 'P' 和模式串中的 'P',如下图所示。可以看出文本串中的 'P' 和模式串中的 'P' 匹配,则 "PLE" 为好后缀,"PLE" 在模式串中的位置为 6(编号从 0 开始)。

  1. 继续比较前面一位,即文本串中的 'M' 和模式串中的 'M',如下图所示。可以看出文本串中的 'M' 和模式串中的 'M' 匹配,则 "MPLE" 为好后缀。"MPLE" 在模式串中的位置为 6(编号从 0 开始)。

  1. 继续比较前面一位,即文本串中的 'I' 和模式串中的 'A',如下图所示。可以看出文本串中的 'I' 和模式串中的 'A' 不匹配。

此时,如果按照「坏字符规则」,模式串应该向右移动 2 - (-1) = 3 位。但是根据「好后缀规则」,我们还有更好的移动方法。

在好后缀 "MPLE" 和好后缀的后缀 "PLE""LE""E" 中,只有好后缀的后缀 "E" 和模式串中的前缀 "E" 相匹配,符合好规则的第二种情况。好后缀的后缀 "E" 的最后一个字符在模式串中的位置为 6,最长前缀 "E"的最后一个字符出现的位置为 0,则根据「好后缀规则」,可以将模式串直接向右移动 6 - 0 = 6 位。如下图所示。

  1. 继续从模式串的尾部开始逐位比较,如下图所示。

可以看出,'P''E' 不匹配,'P' 是坏字符。根据「坏字符规则」,可以将模式串直接向右移动 6 - 4 = 2 位,如下图所示。

  1. 继续从模式串的尾部开始逐位比较,发现模式串全部匹配,于是搜索结束,返回模式串在文本串中的位置。

4. Boyer Moore 算法步骤

整个 BM 算法步骤描述如下:

  1. 计算出文本串 T 的长度为 n,模式串 p 的长度为 m
  2. 先对模式串 p 进行预处理,生成坏字符位置表 bc_table 和好后缀规则后移位数表 gs_talbe
  3. 将模式串 p 的头部与文本串 T 对齐,将 i 指向文本串开始位置,即 i = 0j 指向模式串末尾位置,即 j = m - 1,然后从模式串末尾位置开始进行逐位比较。
    1. 如果文本串对应位置 T[i + j] 上的字符与 p[j] 相同,则继续比较前一位字符。
      1. 如果模式串全部匹配完毕,则返回模式串 p 在文本串中的开始位置 i
    2. 如果文本串对应位置 T[i + j] 上的字符与 p[j] 不相同,则:
      1. 根据坏字符位置表计算出在「坏字符规则」下的移动距离 bad_move
      2. 根据好后缀规则后移位数表计算出在「好后缀规则」下的移动距离 good_mode
      3. 取两种移动距离的最大值,然后对模式串进行移动,即 i += max(bad_move, good_move)
  4. 如果移动到末尾也没有找到匹配情况,则返回 -1

5. Boyer Moore 算法代码实现

BM 算法的匹配过程实现起来并不是很难,而整个算法实现的难点在于预处理阶段的「生成坏字符位置表」和「生成好后缀规则后移位数表」这两步上。尤其是「生成好后缀规则后移位数表」,实现起来十分复杂。下面我们一一进行讲解。

5.1 生成坏字符位置表代码实现

生成坏字符位置表的代码实现比较简单。具体步骤如下:

  • 使用一个哈希表 bc_tablebc_table[bad_char] 表示坏字符 bad_char 在模式串中出现的最右位置。

  • 遍历模式串,以当前字符 p[i] 为键,所在位置下标为值存入字典中。如果出现重复字符,则新的位置下标值会将之前存放的值覆盖掉。这样哈希表中存放的就是该字符在模式串中出现的最右侧位置。

这样如果在 BM 算法的匹配过程中,如果 bad_char 不在 bc_table 中时,可令 bad_char 在模式串中出现的最右侧位置为 -1。如果 bad_charbc_table 中时,bad_char 在模式串中出现的最右侧位置就是 bc_table[bad_char]。这样就可以根据公式计算出可以向右移动的位数了。

生成坏字符位置表的代码如下:

# 生成坏字符位置表
# bc_table[bad_char] 表示坏字符在模式串中最后一次出现的位置
def generateBadCharTable(p: str):bc_table = dict()for i in range(len(p)):bc_table[p[i]] = i                          # 更新坏字符在模式串中最后一次出现的位置return bc_table

5.2 生成好后缀规则后移位数表代码实现

为了生成好后缀规则后移位数表,我们需要先定义一个后缀数组 suffix,其中 suffix[i] = s 表示为以下标 i 为结尾的子串与模式串后缀匹配的最大长度为 s。即满足 p[i-s...i] == p[m-1-s, m-1] 的最大长度为 s

构建 suffix 数组的代码如下:

# 生成 suffix 数组
# suffix[i] 表示为以下标 i 为结尾的子串与模式串后缀匹配的最大长度
def generageSuffixArray(p: str):m = len(p)suffix = [m for _ in range(m)]                  # 初始化时假设匹配的最大长度为 mfor i in range(m - 2, -1, -1):                  # 子串末尾从 m - 2 开始start = i                                   # start 为子串开始位置while start >= 0 and p[start] == p[m - 1 - i + start]:start -= 1                              # 进行后缀匹配,start 为匹配到的子串开始位置suffix[i] = i - start                       # 更新以下标 i 为结尾的子串与模式串后缀匹配的最大长度return suffix

有了 suffix 数组,我们就可以在此基础上定义好后缀规则后移位数表 gs_list。我们使用一个数组来表示好后缀规则后移位数表。其中 gs_list[j] 表示在 j 下标处遇到坏字符时,可根据好规则向右移动的距离。

2.2 好后缀规则 中可知,好后缀规则的移动方式可以分为三种情况。

  • 情况 1:模式串中有子串匹配上好后缀。
  • 情况 2:模式串中无子串匹配上好后缀,但有最长前缀匹配好后缀的后缀。
  • 情况 3:模式串中无子串匹配上好后缀,也找不到前缀匹配。

这 3 种情况中,情况 2 和情况 3 可以合并,因为情况 3 可以看做是匹配到的最长前缀长度为 0。而如果遇到一个坏字符同时满足多种情况,则我们应该选择满足情况中最小的移动距离才不会漏掉可能匹配的情况,比如说当模式串中既有子串可以匹配上好后缀,又有前缀可以匹配上好后缀的后缀,则应该按照前者的方式移动模式串。

  • 为了得到精确的 gs_list[j],我们可以先假定所有情况都为情况 3,即 gs_list[i] = m
  • 然后通过后缀和前缀匹配的方法,更新情况 2 下 gs_list 中坏字符位置处的值,即 gs_list[j] = m - 1 - i,其中 j 是好后缀前的坏字符位置,i 是最长前缀的末尾位置,m - 1 - i 是可向右移动的距离。
  • 最后再计算情况 1 下 gs_list 中坏字符位置处的值,更新在好后缀的左端点处(m - 1 - suffix[i] 处)遇到坏字符可向后移动位数,即 gs_list[m - 1 - suffix[i]] = m - 1 - i

生成好后缀规则后移位数表 gs_list 代码如下:

# 生成好后缀规则后移位数表
# gs_list[j] 表示在 j 下标处遇到坏字符时,可根据好规则向右移动的距离
def generageGoodSuffixList(p: str):# 好后缀规则后移位数表# 情况 1: 模式串中有子串匹配上好后缀# 情况 2: 模式串中无子串匹配上好后缀,但有最长前缀匹配好后缀的后缀# 情况 3: 模式串中无子串匹配上好后缀,也找不到前缀匹配m = len(p)gs_list = [m for _ in range(m)]                 # 情况 3:初始化时假设全部为情况 3suffix = generageSuffixArray(p)                 # 生成 suffix 数组j = 0                                           # j 为好后缀前的坏字符位置for i in range(m - 1, -1, -1):                  # 情况 2:从最长的前缀开始检索if suffix[i] == i + 1:                      # 匹配到前缀,即 p[0...i] == p[m-1-i...m-1]while j < m - 1 - i:if gs_list[j] == m:gs_list[j] = m - 1 - i          # 更新在 j 处遇到坏字符可向后移动位数j += 1for i in range(m - 1):                          # 情况 1:匹配到子串, p[i-s...i] == p[m-1-s, m-1]gs_list[m - 1 - suffix[i]] = m - 1 - i      # 更新在好后缀的左端点处遇到坏字符可向后移动位数return gs_list

5.3 Boyer Moore 算法整体代码实现

# BM 匹配算法
def boyerMoore(T: str, p: str) -> int:n, m = len(T), len(p)bc_table = generateBadCharTable(p)              # 生成坏字符位置表gs_list = generageGoodSuffixList(p)             # 生成好后缀规则后移位数表i = 0while i <= n - m:j = m - 1while j > -1 and T[i + j] == p[j]:          # 进行后缀匹配,跳出循环说明出现坏字符j -= 1if j < 0:return i                                # 匹配完成,返回模式串 p 在文本串 T 中的位置bad_move = j - bc_table.get(T[i + j], -1)   # 坏字符规则下的后移位数good_move = gs_list[j]                      # 好后缀规则下的后移位数i += max(bad_move, good_move)               # 取两种规则下后移位数的最大值进行移动return -1# 生成坏字符位置表
# bc_table[bad_char] 表示坏字符在模式串中最后一次出现的位置
def generateBadCharTable(p: str):bc_table = dict()for i in range(len(p)):bc_table[p[i]] = i                          # 更新坏字符在模式串中最后一次出现的位置return bc_table# 生成好后缀规则后移位数表
# gs_list[j] 表示在 j 下标处遇到坏字符时,可根据好规则向右移动的距离
def generageGoodSuffixList(p: str):# 好后缀规则后移位数表# 情况 1: 模式串中有子串匹配上好后缀# 情况 2: 模式串中无子串匹配上好后缀,但有最长前缀匹配好后缀的后缀# 情况 3: 模式串中无子串匹配上好后缀,也找不到前缀匹配m = len(p)gs_list = [m for _ in range(m)]                 # 情况 3:初始化时假设全部为情况 3suffix = generageSuffixArray(p)                 # 生成 suffix 数组j = 0                                           # j 为好后缀前的坏字符位置for i in range(m - 1, -1, -1):                  # 情况 2:从最长的前缀开始检索if suffix[i] == i + 1:                      # 匹配到前缀,即 p[0...i] == p[m-1-i...m-1]while j < m - 1 - i:if gs_list[j] == m:gs_list[j] = m - 1 - i          # 更新在 j 处遇到坏字符可向后移动位数j += 1for i in range(m - 1):                          # 情况 1:匹配到子串 p[i-s...i] == p[m-1-s, m-1]gs_list[m - 1 - suffix[i]] = m - 1 - i      # 更新在好后缀的左端点处遇到坏字符可向后移动位数return gs_list# 生成 suffix 数组
# suffix[i] 表示为以下标 i 为结尾的子串与模式串后缀匹配的最大长度
def generageSuffixArray(p: str):m = len(p)suffix = [m for _ in range(m)]                  # 初始化时假设匹配的最大长度为 mfor i in range(m - 2, -1, -1):                  # 子串末尾从 m - 2 开始start = i                                   # start 为子串开始位置while start >= 0 and p[start] == p[m - 1 - i + start]:start -= 1                              # 进行后缀匹配,start 为匹配到的子串开始位置suffix[i] = i - start                       # 更新以下标 i 为结尾的子串与模式串后缀匹配的最大长度return suffixprint(boyerMoore("abbcfdddbddcaddebc", "aaaaa"))
print(boyerMoore("", ""))

6. Boyer Moore 算法分析

  • BM 算法在预处理阶段的时间复杂度为 O ( n + σ ) O(n + \sigma) O(n+σ),其中 σ \sigma σ 是字符集的大小。
  • BM 算法在搜索阶段最好情况是每次匹配时,模式串 p 中不存在与文本串 T 中第一个匹配的字符。这时的时间复杂度为 O ( n / m ) O(n / m) O(n/m)
  • BM 算法在搜索阶段最差情况是文本串 T 中有多个重复的字符,并且模式串 p 中有 m - 1 个相同字符前加一个不同的字符组成。这时的时间复杂度为 O ( m ∗ n ) O(m * n) O(mn)
  • 当模式串 p 是非周期性的,在最坏情况下,BM 算法最多需要进行 3 ∗ n 3 * n 3n 次字符比较操作。

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

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

相关文章

数据结构 之 队列(Queue)

​​​​​​​ &#x1f389;欢迎大家观看AUGENSTERN_dc的文章(o゜▽゜)o☆✨✨ &#x1f389;感谢各位读者在百忙之中抽出时间来垂阅我的文章&#xff0c;我会尽我所能向的大家分享我的知识和经验&#x1f4d6; &#x1f389;希望我们在一篇篇的文章中能够共同进步&#xff0…

JAVA爬虫系列

目录 准备工作 yml 1.入门程序&#xff08;获取到静态页面&#xff09; 2.HttpClient---Get 2.1 修改成连接池 3.HttpClient---Get带参数 3.1 修改成连接池 4.HttpClient---Post 4.1 修改成连接池 5.HttpClient---Post带参数 6.HttpClient-连接池 7.设置请求信息 …

蓝桥真题——-小蓝重组质数(全排列和质数判断)

小蓝有一个十进制正整数n&#xff0c;其不包含数码0&#xff0c;现在小蓝可以任意打乱数码的顺序&#xff0c;小蓝想知道通过打乱数码顺序,n 可以变成多少个不同的质数。 #include <iostream> #include<bits/stdc.h> using namespace std; bool isprime(int n) {if…

讯鹏Andon系统解决方案帮助工厂打造生产过程透明化

在现代制造业中&#xff0c;高效透明的生产管理模式对企业的发展至关重要。Andon系统作为一种解决方案&#xff0c;通过软硬件结合的方式&#xff0c;为企业打造了高效透明的生产管理模式&#xff0c;帮助企业实现生产过程的优化和管理的可视化。 Andon系统的软硬件结合为企业提…

swiftUI中的可变属性和封装

swiftUI的可变属性 关于swift中的属性&#xff0c;声明常量使用let &#xff0c; 声明变量使用var 如果需要在swiftUI中更改视图变化那么就需要在 var前面加上state 。 通过挂载到state列表 &#xff0c;从而让xcode找到对应的改变的值 例子&#xff1a; import SwiftUIstruc…

【兆易创新GD32H759I-EVAL开发板】图像处理加速器(IPA)的应用

GD32H7系列的IPA&#xff08;Image Pixel Accelerator&#xff09;是一个高效的图像处理硬件加速器&#xff0c;专门设计用于加速图像处理操作&#xff0c;如像素格式转换、图像旋转、缩放等。它的优势在于能够利用硬件加速来实现这些操作&#xff0c;相比于软件实现&#xff0…

BLE---Service interoperability requirements

0 Preface/Foreword references: Bluetooth core specification V5.4 definition&#xff1a;定义 declaration&#xff1a;声明 1 service definition&#xff08;服务定义&#xff09; 服务定义&#xff08;definition&#xff09;&#xff1a;必须包含服务声明(declara…

【JavaScript】JavaScript 运算符 ① ( 运算符分类 | 算术运算符 | 浮点数 的 算术运算 精度问题 )

文章目录 一、JavaScript 运算符1、运算符分类2、算术运算符3、浮点数 的 算术运算 精度问题 一、JavaScript 运算符 1、运算符分类 在 JavaScript 中 , 运算符 又称为 " 操作符 " , 可以实现 赋值 , 比较 > < , 算术运算 -*/ 等功能 , 运算符功能主要分为以下…

MATLAB中visdiff函数用法

目录 语法 说明 示例 比较两个文件 比较两个文件并指定类型 发布比较报告 visdiff函数的功能是比较两个文件或文件夹。 语法 visdiff(filename1,filename2) visdiff(filename1,filename2,type) comparison visdiff(___) 说明 visdiff(filename1,filename2) 打开比较工…

海格里斯HEGERLS托盘搬运机器人四向车引领三维空间集群设备柔性运维

随着市场的不断迅猛发展变化&#xff0c;在物流仓储中&#xff0c;无论是国内还是海外&#xff0c;都对托盘式解决方案需求量很大。顾名思义&#xff0c;托盘式解决方案简单理解就是将产品放置在托盘上进行存储、搬运和拣选。 面对托盘式方案需求&#xff0c;行业中常见的方案是…

面试常问,ADC,PWM

一 PWM介绍 pwm全名&#xff08;Pulse Width Modulation&#xff09;&#xff1a;脉冲宽度调制 在具有惯性的系统中&#xff0c;可以通过对一系列脉冲的宽度进行调制&#xff0c;来等效地获得所需要的模拟参量&#xff0c;常应用于电机控速等领域。PWM一定程度上是数字到模拟…

Java使用Selenium实现自动化测试以及全功能爬虫

前言 工作中需要抓取一下某音频网站的音频&#xff0c;我就用了两个小时学习弄了一下&#xff0c;竟然弄出来&#xff0c;这里分享记录一下。 springboot项目 Selenium Java使用Selenium实现自动化测试以及全功能爬虫 前言1 自动化测试2 java中集成Selenium3 添加浏览器驱动4…

【linux】进程(一)

先看预备知识&#xff0c;对本篇文章更有帮助。 目录 进程概念&#xff1a;了解动态运行的概念&#xff1a;进程的本身内部属性&#xff1a;启动进程&#xff1a;关闭进程&#xff1a; 如何创建进程&#xff1a;进程状态&#xff1a;直接看进程状态&#xff1a;僵尸进程与孤儿…

llamma笔记:部署Llama2

1 申请Llama2 许可 Download Llama (meta.com) 地址似乎不能填中国 1.1 获取url 提交申请后&#xff0c;填的那个邮箱会受到一封meta发来的邮件&#xff0c;打码部分的url&#xff0c;之后会用得上 2 ubuntu/linux 端部署Llama2 2.1 git clone Llama2的github 仓库 bash g…

git基础命令(四)之分支命令

目录 基础概念git branch-r-a-v-vv-avv重命名分支删除分支git branch -h git checkout创建新的分支追踪远程分支同时切换到该分支创建新的分支并切换到该分支撤销对文件的修改&#xff0c;恢复到最近的提交状态&#xff1a;丢弃本地所有修改git checkout -h git merge合并指定分…

ASP.NET Mvc+FFmpeg+Video实现视频转码

目录 首先&#xff0c;做了视频上传的页面&#xff1a; FFmpeg&#xff1a;视频转码 FFmpegHelper工作类&#xff1a; 后台控制器代码&#xff1a; 前端视图代码&#xff1a; 参考文章&#xff1a; 首先&#xff0c;做了视频上传的页面&#xff1a; 借鉴了这篇文章 ASP.…

D. Tandem Repeats?

思路&#xff1a;首先我们要枚举长度&#xff0c;然后从前往后遍历&#xff0c;判断是否存在改长度的重复串。 代码&#xff1a; void solve(){string s;cin >> s;int n s.size();int ans 0;for(int len n / 2;len > 1;len --){int t 0;for(int i 0;i len <…

TSINGSEE青犀AI智能分析网关V4酿酒厂安全挂网AI检测算法

在酿酒行业中&#xff0c;安全生产一直是企业经营中至关重要的一环。为了确保酒厂生产过程中的安全&#xff0c;TSINGSEE青犀AI智能分析网关V4的安全挂网AI检测算法发挥了重要作用。 TSINGSEE青犀AI智能分析网关V4的安全挂网检测算法是针对酒厂里酒窖挂网行为进行智能检测与识…

个人简历主页搭建系列-03:Hexo+Github Pages 介绍,框架配置

今天的更新内容主要是了解为什么选择这个网站搭建方案&#xff0c;以及一些前置软件的安装。 Why Hexo? 首先我们了解一下几种简单的网站框架搭建方案&#xff0c;看看对于搭建简历网站的需求哪个更合适。 在 BuiltWith&#xff08;网站技术分析工具&#xff09;上我们可以…

【矩阵】73. 矩阵置零【中等】

矩阵置零 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]] 解题思路 1、…