Nickolay.info. Алгоритмы. Алгоритм Бойера-Мура

Алгоритм Бойера-Мура предназначен для решения типовой задачи поиска подстроки в строке. Он считается самым быстрым среди алгоритмов общего назначения, предназначенных для этой задачи. Суть алгоритма - ценой некоторого количества предварительных вычислений над искомой строкой, она сравнивается с исходным текстом не во всех позициях, часть проверок, заведомо не дающих результата, пропускается. Искомый текст индексируется двумя таблицами - таблицей стоп-символов, имеющей длину алфавита и таблицей суффиксов, длина которой соответствует длине шаблона (искомого текста).

Алгоритм неплохо описан в русской "Википедии", предлагается такая его реализация на консольном С++

#include <stdlib.h>
#include <iostream.h>
#include <string.h>
#define ALPHABET_LEN 255 /* Длина алфавита */
#define NOT_FOUND patlen /* Метка "не найдено" в таблице стоп-символов */
#define max(a, b) ((a < b) ? b : a)

//using namespace std;

// Таблица стоп-символов: delta1[c] содержит расстояние между последним
// символом образца с кодом c и правым появлением символа c в шаблоне pat
// Если c отсутствует в шаблоне, то delta1[c] = константе NOT_FOUND
// Если c - это символ string[i] и c != pat[patlen-1], мы можем
// сдвинуть i до delta1[c], который будет минимальным расстоянием,
// на которое можно сдвинуть pat вперед, чтобы получить совпадение string[i]
// с некоторым символом шаблона
// Время работы алгоритма = alphabet_len+patlen шагов
void make_delta1 (int *delta1, char *pat, long int patlen) {
 int i;
 for (i=0; i < ALPHABET_LEN; i++) delta1[i] = NOT_FOUND;
 for (i=0; i < patlen-1; i++) delta1[pat[i]] = patlen-1-i;
}

// Истина, если окончание слова, начиная с word[pos] является префиксом слова
int is_prefix (char *word, int wordlen, int pos) {
 int suffixlen = wordlen - pos;
 // Здесь можно также использовать стандартную strncmp()
 int i;
 for (i = 0; i<suffixlen; i++) {
  if (word[i] != word[pos+i]) return 0;
 }
 return 1;
}

// Длина наибольшего окончания слова, заканчивающегося на word[pos].
// Пример: suffix_length("dddbcabc", 8, 4) = 2
int suffix_length (char *word, int wordlen, int pos) {
 // Увеличиваем длину суффикса i до первого расхождения или начала слова
 int i;
 for (i = 0; (word[pos-i] == word[wordlen-1-i]) && (i < pos); i++);
 return i;
}

// Таблица суффиксов: задает несовпадение pat[pos], мы хотим достигнуть
// следующего возможного вхождения всего шаблона, основываясь на
// известных символах от pat[pos+1] до pat[patlen-1].
// Случай 1:
// От pat[pos+1] до pat[patlen-1] не содержится ничего из шаблона,
// следующее вероятное вхождение шаблона начинается дальше.
// Если подстрока pat[pos+1 .. patlen-1], содержит начало
// шаблона, следующее возможное вхождение шаблона найдено (если есть несколько
// префиксов, берется самый длинный). Иначе следующее возможное вхождение
// начинается после символа, соответствующего pat[patlen-1].
// Случай 2:
// Символы от pat[pos+1] до pat[patlen-1] содержатся в шаблоне.
// Несовпадение говорит, что не найден конец соответствия
// Первый цикл - случай 1, второй - случай 2
void make_delta2 (int *delta2, char *pat, long int patlen) {
 int last_prefix_index = patlen-1;
 // Цикл 1
 int p;
 for (p=patlen-1; p>=0; p--) {
  if (is_prefix(pat, patlen, p+1)) last_prefix_index = p+1;
  delta2[p] = last_prefix_index + (patlen-1 - p);
 }
 // Цикл 2
 for (p=0; p < patlen-1; p++) {
  int slen = suffix_length(pat, patlen, p);
  if (pat[p-slen]!=pat[patlen-1-slen]) delta2[patlen-1-slen]=patlen-1-p+slen;
 }
}

char *boyer_moore (char *string, char *pat) {
 long int stringlen = strlen(string);
 long int patlen =strlen(pat);
 int delta1[ALPHABET_LEN];
 make_delta1(delta1, pat, patlen);
 int *delta2 = new int [patlen * sizeof(int)];
 make_delta2(delta2, pat, patlen);
 int i = patlen-1;
 while (i < stringlen) {
  int j = patlen-1;
  while (j >= 0 && (string[i] == pat[j])) {
   --i; --j;
  }
  if (j < 0) {
   delete delta2;
   return string+i+1;
  }
  i += max(delta1[string[i]], delta2[j]);
 }
 delete delta2;
 return NULL;
}

int main (int argc, char *argv[]) {
 char *string="This is a test string for my test program";
 char *pattern="test";
 cout << "Result=" << boyer_moore(string,pattern) << endl;   
 system("PAUSE");
 return EXIT_SUCCESS;
}

Рейтинг@Mail.ru

вверх гостевая; E-mail