Структуры и алгоритмы обработки данных

       

Алгоритм Боуера и Мура


КМП-поиск дает подлинный выигрыш только тогда, когда неудаче предшествовало некоторое число совпадений. Лишь в этом случае слово сдвигается более чем на единицу. К несчастью, это скорее исключение, чем правило: совпадения встречаются значительно реже, чем несовпадения. Поэтому выигрыш от использования КМП-стратегии в большинстве случаев поиска в обычных текстах весьма незначителен. Метод же, предложенный Р. Боуером и Д. Муром в 1975 г., не только улучшает обработку самого плохого случая, но дает выигрыш в промежуточных ситуациях.

БМ-поиск основывается на необычном соображении v сравнение символов начинается с конца слова, а не с начала. Как и в случае КМП-поиска, слово перед фактическим поиском трансформируется в некоторую таблицу. Пусть для каждого символа x из алфавита величина dx v расстояние от самого правого в слове вхождения x до правого конца слова. Представим себе, что обнаружено расхождение между словом и текстом. В этом случае слово сразу же можно сдвинуть вправо на dpM-1 позиций, т.е. на число позиций, скорее всего большее единицы. Если несовпадающий символ текста в слове вообще не встречается, то сдвиг становится даже больше, а именно сдвигать можно на длину всего слова. Вот пример, иллюстрирующий этот процесс:

Ниже приводится программа с упрощенной стратегией Боуера-Мура, построенная так же, как и предыдущая программа с КМП-алгоритмом. Обратите внимание на такую деталь: во внутреннем цикле используется цикл с repeat, где перед сравнением s и p увеличиваются значения k и j. Это позволяет исключить в индексных выражениях составляющую -1.

Program BM;

const

        Mmax = 100; Nmax = 10000;

var

        i, j, k, M, N: integer;

        ch: char;

        p: array[0..Mmax-1] of char; {слово}

        s: array[0..Nmax-1] of char; {текст}



        d: array[' '..'z'] of integer;


begin

{Ввод текста s и слова p}

Write('N:'); Readln(N);

Write('s:'); Readln(s);

Write('M:'); Readln(M);

Write('p:'); Readln(p);

{Заполнение массива d}

for ch:=' ' to 'z' do d[ch]:=M;

for j:=0 to M-2 do d[p[j]]:=M-j-1;

{Поиск слова p в тексте s}

i:=M;

repeat

          j:=M; k:=i;

          repeat {Цикл сравнения символов }

                     k:=k-1; j:=j-1; {слова, начиная с правого.}

          until (j<0) or (p[j]<>s[k]); {Выход, если сравнили все}

          {слово или несовпадение. }

          i:=i+d[s[i-1]]; {Сдвиг слова вправо }

until (j<0) or (i>N);

{Вывод результата поиска}

if j<0 then Writeln('Yes') {найден }

else Writeln('No'); {не найден}

Readln;

end.

Почти всегда, кроме специально построенных примеров, данный алгоритм требует значительно меньше N сравнений. В самых же благоприятных обстоятельствах, когда последний символ слова всегда попадает на несовпадающий символ текста, число сравнений равно N/M.

Авторы алгоритма приводят и несколько соображений по поводу дальнейших усовершенствований алгоритма. Одно из них v объединить приведенную только что стратегию, обеспечивающую большие сдвиги в случае несовпадения, со стратегией Кнута, Морриса и Пратта, допускающей ощутимые¦ сдвиги при обнаружении совпадения (частичного). Такой метод требует двух таблиц, получаемых при предтрансляции: d1 v только что упомянутая таблица, а d2 v таблица, соответствующая КМП-алгоритму. Из двух сдвигов выбирается больший, причем и тот и другой говорят¦, что никакой меньший сдвиг не может привести к совпадению. Дальнейшее обсуждение этого предмета приводить не будем, поскольку дополнительное усложнение формирования таблиц и самого поиска, кажется, не оправдывает видимого выигрыша в производительности.Фактические дополнительные расходы будут высокими и неизвестно, приведут ли все эти ухищрения к выигрышу или проигрышу.


Содержание раздела