目录
1. 问题定义
在模式匹配问题中,给定长度为 n 的文本字符串 T 和 长度为 m 的模式字符串 P,需要确定 P 是否是 T 的一个子串。如果是,则找到 P 在 T 中的开始位置的索引,或者从 T 中找到所有 P 的开始位置的索引。
2. Horspool 算法
Horspool 算法即 Boyer Moore Horspool 算法,是 Boyer Moore 算法的简化版本。
Horspool 算法通过增加两个启发式算法来提升穷举算法的运行时间。
- looking-glass heuristic(镜像启发式):当比较 P 相对于 T 可能的位置时,从 P的尾部开始比较,然后从后向前移动直到 P 的头部。
- character-jump heuristic(字符跳跃启发式):当比较 P 相对于 T 可能的位置时,对于 T[i]=c, 如果 P 中任何位置都不包含 c,则将 P 完全移动到 T[i] 之后(因为 T[i] 不能匹配 P 中的任何字符);否则,直到 P 中出现字符 c 且与 T[i] 一致才移动 P。
Horspool 算法最坏的情况下运行时间是 O(nm),最好情况下是 O(m + n)。
3. 图解 Horspool
text = "hi hold world"
pattern = "world"
4. 代码实现
4.1. Python
def horspool(text, pattern):
n, m = len(text), len(pattern)
if m == 0:
return 0
last = dict()
for k in range(m):
last[pattern[k]] = k
i = m - 1
k = m - 1
while i < n:
if text[i] == pattern[k]:
if k == 0:
return i
else:
i -= 1
k -= 1
else:
j = last.get(text[i], -1)
i += m - min(k, j + 1)
k = m - 1
return -1
def main():
text = "hi hold world"
pattern1 = "world"
print(horspool(text, pattern1))
pattern2 = "python"
print(horspool(text, pattern2))
if __name__ == "__main__":
main()
4.2. Java
package org.example;
import java.util.HashMap;
public class Horspool {
public static int horspool(String text, String pattern) {
int n = text.length();
int m = pattern.length();
if (m == 0) {
return 0;
}
HashMap<Character, Integer> last = new HashMap<Character, Integer>();
for (int k = 0; k < m; k++) {
last.put(pattern.charAt(k), k);
}
int i = m - 1;
int k = m - 1;
while (i < n) {
if (text.charAt(i) == pattern.charAt(k)) {
if (k == 0) {
return i;
} else {
i -= 1;
k -= 1;
}
} else {
int j = last.containsKey(text.charAt(i)) ? last.get(text.charAt(i)) : -1;
i += m - Math.min(k, j + 1);
k = m - 1;
}
}
return -1;
}
public static void main(String[] args) {
String text = "hi hold world";
String pattern1 = "world";
System.out.println(horspool(text, pattern1));
String pattern2 = "python";
System.out.println(horspool(text, pattern2));
}
}
4.3. C
#include <stdio.h>
#include <string.h>
int min(int a, int b) {
return a < b ? a : b;
}
int horspool(const char *text, const char *pattern) {
int n = strlen(text);
int m = strlen(pattern);
int last[256];
for (int k = 0; k < 256; k++) {
last[k] = -1;
}
for (int k = 0; k < m; k++) {
last[pattern[k]] = k;
}
int i = m - 1;
int k = m - 1;
while (i < n) {
if (text[i] == pattern[k]) {
if (k == 0) {
return i;
} else {
i--;
k--;
}
} else {
int j = last[text[i]];
i += m - min(k, j + 1);
k = m - 1;
}
}
return -1;
}
int main() {
char text[] = "hi hold world";
char pattern1[]= "world";
printf("%d\n", horspool(text, pattern1));
char pattern2[] = "python";
printf("%d\n", horspool(text, pattern2));
}
4.4. C++
#include <iostream>
#include <map>
using namespace std;
int horspool(string text, string pattern) {
int n = text.size();
int m = pattern.size();
if (m == 0) {
return 0;
}
map<char, int> last;
for (int k = 0; k < m; k++) {
last[pattern[k]] = k;
}
int i = m - 1;
int k = m - 1;
while (i < n) {
if (text[i] == pattern[k]) {
if (k == 0) {
return i;
} else {
i--;
k--;
}
} else {
int j = last.find(text[i]) != last.end() ? last[text[i]] : -1;
i += m - min(k, j + 1);
k = m - 1;
}
}
return -1;
}
int main() {
string text = "hi hold world";
string pattern1 = "world";
cout << horspool(text, pattern1) << endl;
string pattern2 = "python";
cout << horspool(text, pattern2) << endl;
}