Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
algorithms.doc
Скачиваний:
29
Добавлен:
06.12.2018
Размер:
9.73 Mб
Скачать
    1. Алгоритм поиска подстроки Бойера-Мура (на основе стоп-символов/безопасных суффиксов)

Алгоритм напоминает элементарный алгоритм поиска подстроки в строке. Основное отличие – сравнение искомой строки W с соответствующей частью строки S осуществляется не слева направо, а справа налево. По результатам сравнения делается вывод – на сколько можно сместиться вправо для сравнения W со следующей подстрокой S (в элементарном алгоритме поиска сдвиг всегда происходит на одну позицию). При этом, есть два независимых алгоритма, позволяющих вычислять, на сколько можно смещаться вправо для сравнения со следующей подстрокой S. Выбирается максимальный из сдвигов, получаемых по этим алгоритмам. Рассмотрим эти два алгоритма.

      1. Эвристика стоп-символа

Рассмотрим несколько примеров.

Пример 1. S=”ababababa”, W=”cccc”.

Сначала сравниваем суффикс S4 и W (S4 - подстрока S,состоящая из ее первых четырех символов). Как уже отмечалось, сначала сравниваем S[3] и W[3]. Они не равны, следовательно, S4 и W не равны. Более того, т.к. S[3] вообще не встречается в W, то далее можно сравнивать с W уже S4+strlen(W)=S8, т.к. суффиксы S4+1,…, S4+strlen(W)-1 заведомо не совпадают с W.

Пример 2. S=”ababacaba”, W=”abac”.

Сначала сравниваем суффикс S4 и W. Как уже отмечалось, сначала сравниваем S[3] и W[3]. Они не равны, следовательно, S4 и W не равны. S[3] встречается первый раз в W (при просмотре с конца) в позиции 1, то далее можно сравнивать с W уже S4+strlen(W)-1-1=S6, т.к. при таком сдвиге впервые S[3] совпадет с соответствующим символом W.

Вообще говоря, пусть сравнивается суффикс Sk и W. Пусть W[l] – первый справа символ W, не совпавший с соответствующим символом строки S, т.е.

S[k-strlen(W)+l]!=W[l], S[k-strlen(W)+i]==W[i] (strlen(W)>i>l).

Если l==0, то мы нашли вхождение W в S. Переходим к анализу Sk+1.

Рассмотрим случай l>0. Обозначим s= S[k-strlen(W)+l] .

Пусть m(s) – функция, выдающая самое правое вхождение символа s в строку W. В случае, если символ s в строке W не найден, пусть m(s)=-1. Тогда, следующим претендентом на сравнение будет Sk+MAX(1,strlen(W)-1-m(s)).

Здесь и далее MAX и MIN в языке С можно определить следующим образом

#define MAX(a,b) ((a)>(b)?(a):(b))

#define MIN(a,b) ((a)<(b)?(a):(b))

Функция m является табличной, поэтому ее можно заменить соответствующим массивом. Например, если мы производим поиск строк в языке С, то m можно определить следующим образом

unsigned char m[256];

Все значения массива изначально инициализируются значением -1. Далее для всех символов W[i] строки W от первого до последнего следует положить

m[W[i]]=MAX(m[W[i]],i);

На самом деле, с точки зрения языка С, последнее утверждение не верно!!! Это связано с тем, что переменная m[k] , вообще говоря, знаковая. Следующая попытка исправить ситуацию тоже не верна

m[(unsigned)W[i]]=MAX(m[(unsigned)W[i]],i);

Связано это с тем, что преобразование

signed char -> unsigned int

происходит, на самом деле, более сложно:

signed char -> signed int -> unsigned int

в результате получаем, что отрицательное 8-битное число преобразуется, сначала, в отрицательное 32-битное число, а только потом произойдет преобразование к беззнаковому числу. Итого

а’=-32 -> 4294967264

Правильное преобразование показано в следующей функции, вычисляющей массив m

void MakeM(char W[], int l, int m[256])

{int i;

for(i=0;i<256;i++)m[i]=-1;

for(i=0;i<l;i++)m[(unsigned char)W[i]]=MAX(m[(unsigned char)W[i]],i);

}

Осталось написать подпрограмму, осуществляющую поиск первого вхождения строки W длины lW в строку S длины lS

char *Search(char S[], int lS, char W[], int lW, int m[256])

{int l,k; if(lS<lW)return NULL;

for(k=lW;k<=lS;k=k+MAX(1,strlen(W)-1-m[(unsigned char)W[l]]))

{

for(l=lW-1;l>=0;l--)if(W[l]!=S[k-lW+l])break;

if(l<0)return S+k-lW;

}

return NULL;

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]