Boyer Moore 算法介绍

news2024/11/17 12:40:08

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)]                  # 初始化时假设匹配的最大长度为 m
    for 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:初始化时假设全部为情况 3
    suffix = 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 += 1
        
    for 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 = 0
    while i <= n - m:
        j = m - 1
        while j > -1 and T[i + j] == p[j]:          # 进行后缀匹配,跳出循环说明出现坏字符
            j -= 1
        if 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:初始化时假设全部为情况 3
    suffix = 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 += 1
        
    for 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)]                  # 初始化时假设匹配的最大长度为 m
    for 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

print(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.coloradmin.cn/o/1521991.html

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

相关文章

【Machine Learning】Suitable Learning Rate in Machine Learning

一、The cases of different learning rates: In the gradient descent algorithm model: is the learning rate of the demand, how to determine the learning rate, and what impact does it have if it is too large or too small? We will analyze it through the follow…

【安全类书籍-1】asp代码审计.pdf

目录 内容简介 作用 下载地址 内容简介 这个文档摘录片段主要讨论了ASP编程中的安全性审计,包括SQL注入漏洞、Cookie注入防范措施及文件上传安全问题,并给出了相关示例代码。 SQL注入漏洞与防范 - ASP代码中展示了如何通过`Request.QueryString`和`Request.Form`获取用户…

SpringBoot打造企业级进销存储系统 第五讲

package com.java1234.repository;import com.java1234.entity.Menu; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query;import java.util.List;/*** 菜单Repository接口*/ public interface MenuReposit…

spacy进行简单的自然语言处理的学习

自然语言处理基本概念 概念&#xff1a;自然语言处理&#xff0c;是让机器理解人的语言的过程。 作用&#xff1a;通过使用自然语言处理&#xff0c;机器可以理解人的语言&#xff0c;从而进行语义分析&#xff0c;例如&#xff1a;从一句话中判断喜怒哀乐&#xff1b;从一段文…

电大搜题:开启学习新时代

身处信息化时代&#xff0c;学习的方式已经发生了巨大的变革。在这个多元化的学习环境中&#xff0c;传统的学习模式已经无法满足现代学习者的需求。然而&#xff0c;电大搜题应运而生&#xff0c;为学习者提供了一个高效、便捷的学习途径。 电大搜题&#xff0c;作为黑龙江开…

阅读 - 二维码扫码登录原理

在日常生活中&#xff0c;二维码出现在很多场景&#xff0c;比如超市支付、系统登录、应用下载等等。了解二维码的原理&#xff0c;可以为技术人员在技术选型时提供新的思路。对于非技术人员呢&#xff0c;除了解惑&#xff0c;还可以引导他更好地辨别生活中遇到的各种二维码&a…

铁路订票平台小程序|基于微信小程序的铁路订票平台小程序设计与实现(源码+数据库+文档)

铁路订票平台小程序目录 目录 基于微信小程序的铁路订票平台小程序设计与实现 一、前言 二、系统设计 三、系统功能设计 1、用户信息管理 2、车次信息管理 3、公告信息管理 4、论坛信息管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐…

Transformer学习笔记(二)

一、文本嵌入层Embedding 1、作用&#xff1a; 无论是源文本嵌入还是目标文本嵌入&#xff0c;都是为了将文本中词汇的数字表示转变为向量表示&#xff0c;希望在这样的高维空间捕捉词汇间的关系。 二、位置编码器Positional Encoding 1、作用&#xff1a; 因为在Transformer…

冲动是魔鬼,工作不顺心时不要把坏脾气带给家人

今天与一个跟踪了很久的客户准备签合同了&#xff0c;客户突然反悔&#xff0c;为此与他周旋了一整天&#xff0c;忙碌得一口水都没有喝。回到小区坐在车里抽着烟&#xff0c;久久不愿回家&#xff0c;只想一个人坐着&#xff0c;疲惫、无奈。这个月的奖金似乎又将成为泡影。 …

Microsoft SQL Server2019占用大量磁盘空间的解决办法(占了我C盘120G的空间!!!)附SQL数据库定时清理代理作业

一、问题 安装Microsoft SQL Server2019后我的C盘在几天后少了100G&#xff0c;如图所示&#xff1a; 解决后&#xff1a; 出现这种情况&#xff0c;我在各种清理C盘后&#xff0c;空间还是没有太大变化 &#xff0c;且几乎每天都要少2个G&#xff0c;后来终于找见原因了&…

Postman接口测试:API 测试的必备技巧

在现代软件开发生命周期中&#xff0c;接口测试是一个至关重要的部分。使用 Postman 这一工具&#xff0c;可以轻松地进行 接口测试。以下是一份简单的使用教程&#xff0c;帮助你快速上手。 安装 Postman 首先&#xff0c;你需要在电脑上安装 Postman。你可以从官网上下载并…

虚拟机NAT模式配置

注意这里IP要和网关在同一网段&#xff0c;且虚拟机默认网关末尾为.2&#xff08;如果默认网关配置为.1会与宿主机冲突&#xff0c;导致无法ping通外网&#xff09; 点击NAT模式下的NAT设置即可查看默认网关 这里的网关可以理解为主机与虚拟机交互的入口

CSDN首发Chainlink(预言机)讲解:基础知识总结 到底什么是预言机本篇带你解析

苏泽 大家好 这里是苏泽 一个钟爱区块链技术的后端开发者 本篇专栏 ←持续记录本人自学两年走过无数弯路的智能合约学习笔记和经验总结 如果喜欢拜托三连支持~ 前面的专栏带大家熟悉了 区块链的基本组成 、共识机制、智能合约、最小信任机制 以及EVM等知识 如遇不懂的概念或名…

2024年【危险化学品经营单位主要负责人】新版试题及危险化学品经营单位主要负责人复审考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【危险化学品经营单位主要负责人】新版试题及危险化学品经营单位主要负责人复审考试&#xff0c;包含危险化学品经营单位主要负责人新版试题答案和解析及危险化学品经营单位主要负责人复审考试练习。安全生产模…

Kubernetes operator系列:webhook 知识学习

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 Kubernetes operator学习 系列文章&#xff0c;本节会对 kubernetes webhook 知识进行学习 本文的所有代码&#xff0c;都存储于github代码库&#xff1a;https://github.com/graham924/share-code-operator-st…

说下你对TCP以及TCP三次握手四次挥手的理解?

参考自简单理解TCP三次握手四次挥手 什么是TCP协议&#xff1f; TCP( Transmission control protocol )即传输控制协议&#xff0c;是一种面向连接、可靠的数据传输协议&#xff0c;它是为了在不可靠的互联网上提供可靠的端到端字节流而专门设计的一个传输协议。 面向连接&a…

【Python】进阶学习:基于Numpy实现按指定维度拼接两个数组

【Python】进阶学习&#xff1a;基于Numpy实现按指定维度拼接两个数组 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希…

无限自动出兵-入门版【war3地图编辑器】

文章目录 1、创建单位和地区2、新事件开端3、动作3.1、创建单位3.2、选取单位3.2.1、发布指令 4、最终 1、创建单位和地区 2、新事件开端 创建新的触发器→新事件开端→时间→时间周期事件 3、动作 3.1、创建单位 3.2、选取单位 单位组→选取单位组内单位做动作 矩形区域内的…

idea中database的一些用法

1、查看表结构 方法1&#xff0c;右键&#xff0c;选这个 方法2 双击表后&#xff0c;看到数据&#xff0c;点DDL 方法3 写SQL时&#xff0c;把鼠标放在表名上&#xff0c;可以快速查看表结构 2、表生成对应的实体类 表中右键&#xff0c;选择这2个&#xff0c;选择生成的路…

STM32-Flash闪存

简介 STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分&#xff0c;通过闪存存储器接口&#xff08;外设&#xff09;可以对程序存储器和选项字节进行擦除和编程。 读写Flash的用途 1.利用程序存储器的剩余空间来保存掉电不丢失的用户数据。 2.通过在程序中…