Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Алгоритмы и структуры данных.doc
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
1.14 Mб
Скачать

3.3Поиск в строке. Алгоритм боуера-мура

Алгоритм Боуера-Мура (БМ алгоритм) предложен в 1975 г. Р. Боуером и Д. Муром. Основная идея алгоритма состоит в организации посимвольного сравнения элементов строки и слова (подстроки) начиная с конца слова.

Для организации поиска перед началом поиска искомое слово (подстрока) трансформируется в некоторую таблицу поиска. Для наглядности будем считать, что таблица состоит из двух строк.

В качестве элементов первой строки такой таблицы используется элементы кодовой таблицы в 128 символов ASCII или в 256 символов расширенной кодовой таблицы.

Вторая строка таблицы формируется следующим образом:

  • Для каждого символа таблицы, являющегося символом искомого слова, в таблицу заносится расстояние от самого правого вхождения этого символа в искомое слово до конца этого слова.

  • Исключение составляет последний символ искомого слова. Он не принимает участия в описанной выше обработке. Если подобный ему символ или символы есть внутри слова, то именно они принимают участие в обработке. Если подобных ему символов нет внутри слова, то последний символ слова обрабатывается как символ, не являющийся символом искомого слова.

  • Для каждого символа таблицы, не являющегося символом искомого слова, в таблицу заносится длина искомого слова.

Так, например, для искомого слова "информационные" для символа и задается расстояние 5, для символа н -расстояние 2, а для символа е – расстояние 14.

Фрагмент таблицы поиска, содержащий символы слова "информационные" имеет вид:

а

б

д

е

и

к

м

н

о

п

р

с

у

ф

ы

7

14

14

14

5

14

14

2

4

14

9

14

14

11

1

Процесс поиска реализуется следующим образом.

  1. Выполняется посимвольное сравнение элементов строки и искомого слова (подстроки), начиная с конца слова.

  2. Если при посимвольном сравнении элементов строки и слова обнаружено расхождение между образом S и строкой P, т.е. имеет место условие S[i] <> P[j], то анализируется символ строки поиска S[i], для которого произошло несовпадение.

  3. Если символ строки поиска S[i] входит в образ P, то образ P сразу сдвигается вправо относительно строки поиска S на количество позиций, соответствующих символу S[i] в таблице поиска. Таким образом, сдвиг выполняется так, чтобы символы на соответствующих позициях в строке и слове совпадали. Чаще всего сдвиг выполняется на количество позиций больше 1.

  4. Если символ строки поиска S[i] не встречается внутри слова P (хотя может быть последним символом слова P), то образ P сразу сдвигается вправо относительно строки поиска S на всю свою длину.

Пример. Пусть задана строка S:

высокопроизводительная система обработки информации

В этой строке необходимо найти слово P:

обработки

Тогда процесс поиска будет иметь вид:

высокопроизводительная система обработки информации

обработки

Несовпадение в строке S по символу о и сдвиг на 3 позиции так, чтобы символы на соответствующих позициях в строке и слове совпадали.

обработки

Несовпадение в строке S по символу в и, так как символ в не входит в слово, то сдвиг на всю длину слова.

обработки

Несовпадение в строке S по символу а и сдвиг на 5 позиций так, чтобы символы на соответствующих позициях в строке и слове совпадали.

обработки

Несовпадение в строке S по символу с и сдвиг на всю длину строки и, так как символ с не входит в слово, то сдвиг на всю длину слова.

обработки

Несовпадение в строке S по символу а и сдвиг на 5 позиций так, чтобы символы на соответствующих позициях в строке и слове совпадали.

обработки

Обнаружено вхождение слова P в строку S.

Приведенный выше алгоритм работает хорошо независимо от длины слова (подстроки) поиска и типа строки поиска. Так в рассматриваемом примере общее количество сравнений составило 14 = 5 + 9. Из них 5 – количество сравнений до начала совпадения с образом и 9 – количество сравнений, определяемых длиной образа.

Примечание. Вместо предложенной сложной схемы сдвигов можно применять и более упрощенную схему. При возникшем рассогласовании S[i] <> P[j] сдвиг образа P выполняется до первого с конца символа в образе, который совпадает с символом строки S[i], по которому произошло рассогласование. Если символ строки S[i], по которому произошло рассогласование, не входит в образ, то сдвиг выполняется на всю длину образа.

Ниже приводится программа, реализующая алгоритм Боуера-Мура, выполняющего сдвиг образа P вправо относительно строки поиска S на количество позиций, соответствующих в таблице поиска символу P[j], для которого произошло несовпадение, или на длину всего образа (слова), если символ S[i] не входит в состав искомого образа.

Таблица поиска реализуется в виде одномерного массива Shift, который содержит 256, элементов, пронумерованных в диапазоне от 0 до 255. Таким образом, порядковый номер элемента массива соответствует порядковому номеру соответствующего символа в кодовой таблице. Для каждого, кроме последнего, символа искомого слова в соответствующие позиции массива Shift заносится расстояние от последнего вхождения этого символа в искомое слово до конца слова, а для всех остальных символов массива Shift – длина искомого слова.

Ниже приводится процедура поиска подстроки в строке методом Боуера-Мура (БМ). Процедура в качестве параметров получает исходную строку Strng и образ поиска Pattern, а возвращает позицию Position в исходной строке, начиная с которой выполняется посимвольное совпадение элементов этой строки и элементов образа, а также признак успешности поиска Find.

procedure BMSearchSubString (const Strng: string; const Pattern: string;

var Position: integer; var Find: Boolean);

var J: integer;

LengthStr: integer; {длина строки поиска}

LengthPat: integer; {длина образа поиска}

StartComp: integer; {позиция начала сравнения – позиция текущего базирования конца образа в строке поиска}

CmpChrStr: integer; {позиция символа, подвергаемого сравнению, в строке поиска}

CmpChrPat: integer; {позиция символа, подвергаемого сравнению, в образе поиска}

Ch: char;

Shift: array [0..255] of integer; {таблица, которая для каждого символа, входящего в образ, содержит расстояние от его самого правого вхождения в образ до конца образа. Используется для определения количества позиций в строке поиска, на которые необходимо сдвинуть образ поиска вправо при частичном несовпадении образа поиска с фрагментом строки поиска}

begin

LengthStr := Length (Strng);

LengthPtr := Length (Pattern);

Find := false;

{подготовка массива Shift}

for J := 0 to 255 do Shift[J] := LengthPat; {В каждый элемент массива заносится длина искомого образа}

{корректировка массива Shift с первого по предпоследний символы искомого образа}

for J := 0 to LengthPat - 2 do Shift[Ord(Pattern[J])] := LengthPat – J - 1;

{Для каждого символа, входящего в образ (кроме последнего), в массиве Shift задается расстояние от его текущего вхождения в образ до конца образа. В результате для каждого такого символа, получим расстояние от его самого правого вхождения в образ до конца образа}

StartComp := LengthPat;

J := 0;

repeat

{начало очередного сравнения с образом поиска}

CmpChrPat := LengthPat;

CmpChrStr := StartComp;

repeat

CmpChrStr := CmpChrStr - 1;

CmpChrPat := CmpChrPat - 1;

until (CmpChrPat < 0) or (Pattern[CmpChrPat] <> Strng[CmpChrStr]);

{Сравнение выполняется до тех пор, пока не произойдет совпадение образа поиска и подстроки в строке поиска (первое условие) или пока не произойдет несовпадение очередных сравниваемых символов в строке и образе поиска (второе условие)}

{Сдвиг позиции начала сравнения в строке поиска}

StartComp := StartComp + Shift[Ord(Strng[CmpChrStr])]; {Сдвиг на количество позиций, соответствующих символу Strng[CmpChrStr] в таблице поиска или сдвиг на длину всего образа}

{ StartComp := StartComp + Shift[Ord(Strng[StartComp-1])]; Сдвиг до первого с конца символа в образе поиска, который совпадает с Strng[StartComp-1], или сдвиг на длину всего образа}

until (CmpChrPat < 0) or (StartComp > LengthStr); {Сравнение выполняется до тех пор, пока не произойдет совпадение образа поиска и подстроки в строке поиска или пока не исчерпается строка поиска}

if CmpChrPat < 0 then Find := true;

end; {BMSearchSubString}

Далее приводится текст программы, которая реализует ввод с терминала исходной строки и образа и размещает их в соответствующих массивах типа char, осуществляет поиск вхождения образа в строку методом Боуэра-Мура и одновременно выводит на экран уже рассмотренный фрагмент строки, что позволяет проиллюстрировать процесс поиска, как показано в приводимом выше примере.

program SearchSubString; {алгоритм Боуера-Мура (БМ)}

uses Crt;

const NMax = 1000;

MMax = 100;

type TFile = Text;

var J: integer;

LengthStr: integer; {длина строки поиска}

LengthPat: integer; {длина образа поиска}

StartComp: integer; {позиция начала сравнения – позиция текущего базирования конца образа в строке поиска}

CmpChrStr: integer; {позиция символа, подвергаемого сравнению, в строке поиска}

CmpChrPat: integer; {позиция символа, подвергаемого сравнению, в образе поиска}

Ch: char;

Strng: array [0..NMax-1] of char; {строка поиска}

Pattern: array [0..MMax-1] of char; {образ поиска}

Shift: array [0..255] of integer; {таблица, смещений}

FileName: string;

Fi: TFile; {Ввод с клавиатуры как из файла произвольной длины}

begin

Clrscr;

WriteLn;

WriteLn ('Ввод строки поиска: ');

WriteLn ('Для завершения ввода строки поиска нажмите клавишу CTRL+Z');

WriteLn;

FileName := 'Con';

Assign (Fi, FileName);

Reset (Fi);

{ввод строки поиска}

LengthStr := 0;

while not EOF (Fi) do

begin

Read (Fi, Ch);

Strng[LengthStr] := Ch;

LengthStr := LengthStr+1;

end;

WriteLn;

{поочередный ввод одного или нескольких образов поиска}

while true do

begin

WriteLn ('Ввод очередного образа поиска (подстроки): ');

WriteLn ('Для завершения ввода образа поиска (подстроки) нажмите клавишу CTRL+Z');

WriteLn;

FileName := 'Con';

Assign (Fi, FileName);

Reset (Fi);

{Ввод очередного образа поиска}

LengthPat := 0;

while not EOF (Fi) do

begin

Read (Fi, Ch);

Pattern[LengthPat] := Ch;

LengthPat := LengthPat + 1;

end;

{подготовка массива Shift}

for J := 0 to 255 do

Shift[J] := LengthPat; {В каждый элемент массива заносится длина искомого образа}

{корректировка массива Shift с первого по предпоследний символы искомого образа}

for J := 0 to LengthPat - 2 do

Shift[Ord(Pattern[J])] := LengthPat – J - 1;

StartComp := LengthPat;

J := 0;

repeat

while J < StartComp do {Последовательно выводятся очередные части строки до позиции StartComp - начала очередного сравнения с образом поиска}

begin

Write (Strng[J]);

J := J + 1;

end;

{начало очередного сравнения с образом поиска}

CmpChrPat := LengthPat;

CmpChrStr := StartComp;

repeat

CmpChrStr := CmpChrStr - 1;

CmpChrPat := CmpChrPat - 1;

until (CmpChrPat < 0) or (Pattern[CmpChrPat] <> Strng[CmpChrStr]);

{Сдвиг позиции начала сравнения в строке поиска}

StartComp := StartComp + Shift[Ord(Strng[CmpChrStr])];

until (CmpChrPat < 0) or (StartComp > LengthStr);

if CmpChrPat < 0

then WriteLn ('!! ОБРАЗ НАЙДЕН !!')

else

begin

while J <= LengthStr - 1 do {Выводится остаток строки поиска т.к. позиция StartComp вышла за пределы строки}

begin

Write (Strng[J]);

J := J+1;

end;

WriteLn ('?? ОБРАЗ НЕ НАЙДЕН ??');

end;

WriteLn;

WriteLn ('Для завершения поиска нажмите клавишу Esc, иначе другую клавишу');

if ReadKey = Char(27) then Exit;

end;

end.