C语言实现BM字符串匹配算法:从基础到实践
简介
在字符串处理领域,字符串匹配算法是一项核心技术。BM(Boyer-Moore)字符串匹配算法作为一种高效的字符串搜索算法,它的独特之处在于它从右向左进行字符比较,通过利用坏字符规则和好后缀规则,能够跳过大量不必要的比较,从而大大提高匹配效率。本文将详细介绍如何使用C语言实现BM字符串匹配算法,帮助读者深入理解并应用这一强大的算法。
目录
- 基础概念
- 坏字符规则
- 好后缀规则
- 使用方法
- 预处理函数
- 匹配函数
- 常见实践
- 简单文本匹配
- 文件内容搜索
- 最佳实践
- 优化预处理
- 减少内存使用
- 代码示例
- 小结
- 参考资料
基础概念
坏字符规则
在BM算法中,坏字符是指在文本串和模式串从右向左比较时,第一个不匹配的字符。当发现坏字符时,我们可以根据坏字符在模式串中最右出现的位置,将模式串向右移动一定的距离,使得坏字符对应的模式串位置能与文本串中的坏字符对齐(如果可能)。
好后缀规则
好后缀是指在文本串和模式串从右向左比较时,已经匹配的后缀部分。当出现不匹配时,根据好后缀在模式串中其他位置的出现情况,将模式串向右移动。好后缀规则比坏字符规则更为复杂,但它能提供更大的移动距离,进一步提高匹配效率。
使用方法
预处理函数
在进行实际匹配之前,我们需要对模式串进行预处理,构建坏字符表和好后缀表。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_CHAR 256
// 构建坏字符表
void buildBadCharTable(char *pattern, int badchar[MAX_CHAR]) {
int len = strlen(pattern);
for (int i = 0; i < MAX_CHAR; i++) {
badchar[i] = -1;
}
for (int i = 0; i < len; i++) {
badchar[(int)pattern[i]] = i;
}
}
// 构建好后缀表(简化版本,仅展示思路)
void buildGoodSuffixTable(char *pattern, int *suffix, int *goodSuffix) {
int m = strlen(pattern);
int i, j;
// 计算后缀数组
for (i = 0; i < m; i++) {
suffix[i] = 0;
}
for (i = m - 1; i >= 0; i--) {
j = i;
while (j >= 0 && pattern[j] == pattern[m - 1 - (i - j)]) {
j--;
}
suffix[i] = i - j;
}
// 计算好后缀数组
for (i = 0; i < m; i++) {
goodSuffix[i] = m;
}
for (i = m - 1; i >= 0; i--) {
if (suffix[i] == i + 1) {
for (j = 0; j < m - 1 - i; j++) {
if (goodSuffix[j] == m) {
goodSuffix[j] = m - 1 - i;
}
}
}
}
for (i = 0; i <= m - 2; i++) {
goodSuffix[m - 1 - suffix[i]] = m - 1 - i;
}
}
匹配函数
使用预处理得到的坏字符表和好后缀表进行字符串匹配。
// BM字符串匹配函数
int boyerMoore(char *text, char *pattern) {
int n = strlen(text);
int m = strlen(pattern);
int badchar[MAX_CHAR];
int *suffix = (int *)malloc(m * sizeof(int));
int *goodSuffix = (int *)malloc(m * sizeof(int));
buildBadCharTable(pattern, badchar);
buildGoodSuffixTable(pattern, suffix, goodSuffix);
int s = 0;
while (s <= n - m) {
int j = m - 1;
while (j >= 0 && pattern[j] == text[s + j]) {
j--;
}
if (j < 0) {
free(suffix);
free(goodSuffix);
return s;
} else {
int x = j - badchar[(int)text[s + j]];
int y = 0;
if (j < m - 1) {
y = goodSuffix[j + 1];
}
s += (x > y)? x : y;
}
}
free(suffix);
free(goodSuffix);
return -1;
}
常见实践
简单文本匹配
在一段简单的文本中查找特定的模式串。
int main() {
char text[] = "ABABDABACDABABCABAB";
char pattern[] = "ABABCABAB";
int result = boyerMoore(text, pattern);
if (result!= -1) {
printf("模式串在文本中出现的位置是: %d\n", result);
} else {
printf("模式串未在文本中找到\n");
}
return 0;
}
文件内容搜索
在一个文件中搜索特定的字符串。
int main() {
FILE *file = fopen("example.txt", "r");
if (file == NULL) {
perror("无法打开文件");
return 1;
}
char text[10000];
fgets(text, sizeof(text), file);
char pattern[] = "search_string";
int result = boyerMoore(text, pattern);
if (result!= -1) {
printf("模式串在文件中出现的位置是: %d\n", result);
} else {
printf("模式串未在文件中找到\n");
}
fclose(file);
return 0;
}
最佳实践
优化预处理
在构建坏字符表和好后缀表时,可以进一步优化算法的时间复杂度。例如,在构建好后缀表时,可以使用更高效的数据结构和算法,减少不必要的计算。
减少内存使用
在构建表的过程中,可以采用一些技巧来减少内存的占用。例如,对于坏字符表,可以使用稀疏数组来存储,只记录出现过的字符的位置。
小结
BM字符串匹配算法是一种高效的字符串匹配算法,通过坏字符规则和好后缀规则,能够显著提高匹配效率。本文详细介绍了BM算法的基础概念、使用方法、常见实践以及最佳实践,并提供了完整的C语言代码示例。希望读者通过阅读本文,能够深入理解并熟练应用BM字符串匹配算法。
参考资料
- 《算法导论》(Thomas H. Cormen等著)
- 维基百科 - Boyer-Moore字符串搜索算法