Fancy DSA Fancy DSA
数据结构
算法
LeetCode
  • 关于
  • 导航 (opens new window)
  • 分类
  • 标签
  • 归档
设计模式 (opens new window)
博客 (opens new window)
GitHub (opens new window)

Jonsam NG

想的更多,也要想的更远
数据结构
算法
LeetCode
  • 关于
  • 导航 (opens new window)
  • 分类
  • 标签
  • 归档
设计模式 (opens new window)
博客 (opens new window)
GitHub (opens new window)
  • 开始上手
  • Plan 计划
  • Roadmap 路线
  • 算法简介
  • Sort 排序

  • Search 搜索

  • Recursive 递归

  • Graph 图

  • Tree 树

  • Math 数学

  • Hash 哈希

  • String 字符串

    • AlphaNumericPalindrome [回文串]
    • AlternativeStringArrange [交替合并字符串]
    • BoyerMoore [博耶-穆尔字符串搜索算法、BM 算法]
      • 介绍
      • 移动规则
      • 演示
      • 实现
      • 扩展
      • 参考
    • CheckAnagram [易位构词]
    • NamingConvention [命名规则]
    • CheckExceeding [Exceeding words]
    • CheckPangram [全字母句]
    • CheckWordOccurrence [单词计数]
    • CountVowels [元音字母计数]
    • CreatePermutations [全排列]
    • DiceCoefficient [Dice系数]
    • FormatPhoneNumber [格式化电话号码]
    • GenerateGUID [生成GUID、UUID]
    • HammingDistance [汉明距离]
    • KMPPatternSearching [KMP字符串匹配]
  • BitManipulation 位操纵

  • Backtracking 回溯

  • DynamicProgramming 动态规划

  • Cache 缓存

  • Array 数组

  • Ciphers 密码学

  • Conversions 转换

  • ProjectEuler 欧拉计划

  • 其他

  • 算法
  • String 字符串
jonsam
2022-09-26
目录

BoyerMoore [博耶-穆尔字符串搜索算法、BM 算法]

# 介绍

在计算机科学里,博耶 - 穆尔字符串搜索算法是一种非常高效的字符串搜索算法。虽然博耶 - 穆尔算法的执行时间同样线性依赖于被搜索字符串的大小,但是通常仅为其它算法的一小部分:它不需要对被搜索的字符串中的字符进行逐一比较,而会跳过其中某些部分。通常搜索关键字越长,算法速度越快。它的效率来自于这样的事实:对于每一次失败的匹配尝试,算法都能够使用这些信息来排除尽可能多的无法匹配的位置。

更巧妙的是,这两个规则的移动位数,只与搜索词有关,与原字符串无关。因此,可以预先计算生成《坏字符规则表》和《好后缀规则表》。使用时,只要查表比较一下就可以了。

# 移动规则

移动字符数是通过两条规则决定的:坏字符规则和好后缀规则。实际移动为通过这两条规则计算出的最大移动个数。

  • 坏字符规则:当 T 中有字符不匹配时,如果 T 中的这个不匹配的字符出现在对应 P 中当前位置的左侧,那么 P 移动位置将这两个在字符对齐。如果 T 中这个不匹配字符不在 P 中当前位置的左侧,那么将当前位置左侧的所有字符均移到该不匹配字符后。

  • 好后缀规则:假设有 P 和 T,T 中字串 t 匹配到了 P 的一个后缀,但在比较位置 i 时发生不匹配。设匹配到的好后缀在 T 中为 t,在 P 中为 t'(t = t')。好后缀规则的实质是,将不匹配位置右侧匹配到的字符串 t 的所有字符后缀组合,用于查找它们在 P 的不匹配位置左侧是否存在。分两种情况来讨论:

    • 在 P 中 i 位置的左侧最靠近 i 位置查找字串 t' 使得 t'=t(此时 t' 不是 P 的后缀,实际上也就是查找匹配到的字串除了在 P 的后缀中存在,是否在 P 的其他位置存在),若存在,则移动相应的位数将找到的 t' 与 T 中的 t 对齐。
    • 如果 t' 不存在,那我们继续查找 t 的某一个后缀是否为 P 的前缀,若存在,则移动相应的位将 P 的前缀与 t 的后缀位置对齐。否则,将 P 向后移动 n 个字符。

# 演示

可视化

  • Visualizing String Matching Algorithms (opens new window)
  • Interactive Demo of the Boyer-Moore String Search Algorithm (opens new window)
  • Boyer-Moore String Search Visualization (opens new window)

# 实现

# JavaScript

只应用坏字符规则。

/*
 *Implementation of the Boyer-Moore String Search Algorithm.
 *The Boyer–Moore string search algorithm allows linear time in
 *search by skipping indices when searching inside a string for a pattern.
 **/
const buildBadMatchTable = (str) => {
  const tableObj = {}
  const strLength = str.length
  for (let i = 0; i < strLength - 1; i++) {
    tableObj[str[i]] = strLength - 1 - i
  }
  if (tableObj[str[strLength - 1]] === undefined) {
    tableObj[str[strLength - 1]] = strLength
  }
  return tableObj
}

const boyerMoore = (str, pattern) => {
  const badMatchTable = buildBadMatchTable(pattern)
  let offset = 0
  const patternLastIndex = pattern.length - 1
  const maxOffset = str.length - pattern.length
  // if the offset is bigger than maxOffset, cannot be found
  while (offset <= maxOffset) {
    let scanIndex = 0
    while (pattern[scanIndex] === str[scanIndex + offset]) {
      if (scanIndex === patternLastIndex) {
        // found at this index
        return offset
      }
      scanIndex++
    }
    const badMatchString = str[offset + patternLastIndex]
    if (badMatchTable[badMatchString]) {
      // increase the offset if it exists
      offset += badMatchTable[badMatchString]
    } else {
      offset++
    }
  }
  return -1
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

更复杂的实现,同时应用坏字符规则和好后缀规则。

/**
 * Returns the index of the first occurence of given string in the phrase
 * In case of no match, returns -1
 * see https://gist.github.com/Kamilczak020/f8382eef9777e8f07d47be29a4efc04b
 * 
 * @param text string to be searched 
 * @param pattern string to be found in the text
 */
function boyerMooreSearch(text: string, pattern: string): number {
    
    // Handle edge case
    if (pattern.length === 0) {
        return -1;
    }

    let charTable = makeCharTable(pattern);
    let offsetTable = makeOffsetTable(pattern);

    for (let i = pattern.length - 1, j; i < text.length;) {
        for (j = pattern.length - 1; pattern[j] == text[i]; i--, j--) {
            if (j === 0) {
                return i;
            }
        }

        const charCode = text.charCodeAt(i);
        i+= Math.max(offsetTable[pattern.length - 1 - j], charTable[charCode]);
    }

    return -1;
}

/**
 * Creates jump table, based on mismatched character information
 */
function makeCharTable(pattern: string): number[] {
    let table = [];

    // 65536 being the max value of char + 1
    for (let i = 0; i < 65536; i++) {
        table.push(pattern.length);
    }

    for (let i = 0; i < pattern.length - 1; i++) {
        const charCode = pattern.charCodeAt(i);
        table[charCode] = pattern.length - 1 - i;
    }

    return table;
}


function makeOffsetTable(pattern: string): number[] {
    let table = [];
    table.length = pattern.length;

    let lastPrefixPosition = pattern.length;

    for (let i = pattern.length; i > 0; i--) {
        if (isPrefix(pattern, i)) {
            lastPrefixPosition = i;
        }

        table[pattern.length - i] = lastPrefixPosition - 1 + pattern.length;
    }

    for (let i = 0; i < pattern.length - 1; i++) {
        const slen = suffixLength(pattern, i);
        table[slen] = pattern.length - 1 - i + slen;
    }

    return table;
}

function isPrefix(pattern: string, p: number): boolean {
    for (let i = p, j = 0; i < pattern.length; i++, j++) {
        if (pattern[i] != pattern[j]) {
            return false;
        }

        return true;
    }
}

function suffixLength(pattern: string, p: number) {
    let len = 0;

    for (let i = p, j = pattern.length - 1; i >= 0 && pattern[i] == pattern[j]; i--, j--) {
        len += 1;
    }

    return len;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

# 扩展

# 如何计算坏字符表和好后缀表?

参见:

  • Calculating Boyer Moore Bad Character Table with examples | by Sulakshana Ranawake | Medium (opens new window)
  • 动画:BM 算法中的坏字符规则与好后缀规则 - 腾讯云开发者社区 - 腾讯云 (opens new window)

# 参考

  • Boyer–Moore string-search algorithm - Wikiwand (opens new window)
  • 博耶 - 穆尔字符串搜索算法 - Wikiwand (opens new window)
  • 字符串匹配的 Boyer-Moore 算法 - 阮一峰的网络日志 (opens new window)
  • Boyer-Moore Fast String Searching Example (opens new window)
  • https://www.cs.utexas.edu/users/moore/publications/fstrpos.pdf (opens new window)
  • https://www.cs.jhu.edu/~langmea/resources/lecture_notes/boyer_moore.pdf (opens new window)
  • Boyer-Moore (opens new window)
编辑 (opens new window)
#[ "ReadAgain" ]
上次更新: 2022/10/20, 20:03:22
AlternativeStringArrange [交替合并字符串]
CheckAnagram [易位构词]

← AlternativeStringArrange [交替合并字符串] CheckAnagram [易位构词]→

最近更新
01
0-20题解
10-31
02
本章导读
10-31
03
算法与转换:Part1
10-28
更多文章>
Theme by Vdoing | Copyright © 2022-2022 Fancy DSA | Made by Jonsam by ❤
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式