Смекни!
smekni.com

Алгоритмы поиска подстроки в строке (стр. 3 из 4)

Начало поиска.

Последний символ образца не совпадает с наложенным символом строки. Сдвигаем образец вправо на 5 позиций:

Три символа образца совпали, а четвертый – нет. Сдвигаем образец вправо на одну позицию:

Последний символ снова не совпадает с символом строки. В соответствии с таблицей смещений сдвигаем образец на 2 позиции:

Еще раз сдвигаем образец на 2 позиции:



Теперь, в соответствии с таблицей, сдвигаем образец на одну позицию, и получаем искомое вхождение образца:

Реализуем указанный алгоритм на языке Pascal.

Прежде всего следует определить тип данных «таблица смещений». Для кодовой таблицы, состоящей из 256 символов, определение этого типа будет выглядеть так:

Type

TBMTable = Array [0..255] of Integer;

Далее приводится процедура, вычисляющая таблицу смещений для образца p (Листинг 5).

Теперь напишем функцию, осуществляющую поиск (Листинг 6).

Параметр StartPos позволяет указать позицию в строке s, с которой следует начинать поиск. Это может быть полезно в том случае, если вы захотите найти все вхождения p в s. Для поиска с самого начала строки следует задать StartPos равным 1. Если результат поиска не равен нулю, то для того, чтобы найти следующее вхождение p в s, нужно задать StartPos равным значению «предыдущий результат плюс длина образца».

1.4.2. Модификации БМ.

Быстрый поиск (Классификация Thierry Lecroq [2]).

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

таблицей ASCII и при обычном поиске в текстовом редакторе, он становится чрезвычайно полезен. Использование в алгоритме только его одного может быть весьма эффективным.

После попытки совмещения x и y [i, i+m-1], длина сдвига - не менее 1. Таким образом, символ y [ i + m ] обязательно будет вовлечен в следующую попытку, а значит, может быть использован в текущей попытке для сдвига плохого символа. Модифицируем функцию плохого символа, чтобы принять в расчет последний символ х:

bc[ a ] = min { j | 0

j
m и x[ m - 1 - j ] = a }, если a встречается в x,

bc[ a ] = m в противоположном случае.

Сравнения текста и образца могут производиться в любом порядке.

Турбо БМ (Классификация Thierry Lecroq [2]).

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

Это даст нам два преимущества:

1. Возможность перескочить через этот сегмент

2. Возможность применения «турбо – сдвига»

«Турбо – сдвиг» может произойти, если мы обнаружим, что суффикс образца, который сходится с текстом, короче, чем тот, который был запомнен ранее.

Пусть u - запомненный сегмент, а v - cуффикс, совпавший во время текущей попытки, такой что uzv - суффикс x. Тогда av - суффикс x, два символа а и b встречаются на расстоянии p в тексте, и суффикс x длины |uzv| имеет период длины p, а значит не может перекрыть оба появления символов а и b в тексте. Наименьший возможный сдвиг имеет длину |u| - |v| ( его мы и называем «турбо – сдвигом» ).

1.5. Поиск подстрок с помощью конечного автомата.

1.5.1. Структура автомата.

По определению, конечный автомат представляет собой пятерку М = (Q, q0, A,

,
), где:

Q — конечное множество состояний;

q0

Q — начальное состояние;

А

Q — конечное множество допускающих состояний;

— конечный входной алфавит;

— функция Q х
Q, называемая функцией переходов автомата.

Первоначально конечный автомат находится в состоянии q0. Затем он по очереди читает символы из входной строки. Находясь в состоянии q и читая символ а, автомат переходит в состояние

(q,a). Если автомат находится в состоянии q
A говорят, что он допускает прочитанную часть входной строки. Если q
А, то прочитанная часть строки отвергнута.

С конечным состоянием М связана функция

, называемая функцией конечного состояния, определяемая следующим образом:
есть состояние, в которое придет автомат (из начального состояния), прочитав строку w. Автомат допускает строку w тогда и только тогда, когда
А

Для каждого образца Р можно построить конечный автомат, ищущий этот образец в тексте. Первым шагом в построении автомата, соответствующего строке-образцу Р[1..m], является построение по Р вспомогательной суффикс-функциии (как в алгоритме КМП). Теперь определим конечный автомат, соответствующий образцу Р[1..m], следующим образом:

· Его множество состояний Q = {0,1,..., m}. Начальное состояние q0 = 0. Единственное допускающее состояние m;

· Функция переходов

определена соотношением (q — состояние,
— символ):
(q,a) =
(Pqa). (1)

Поясним это соотношение. Требуется сконструировать автомат таким образом, чтобы при его действии на строку Т соотношение

i) =
i)

являлось инвариантом (тогда равенство

i) = m будет равносильно тому, что Р входит в Т со сдвигом i — m, и автомат тем самым найдет все допустимые сдвиги). Однако в этом случае вычисление перехода по формуле (1) необходимо для поддержания истинности инварианта, что следует из теорем, приведенных ниже.[3]

Теорема. Пусть q =

(х), где х — строка. Тогда для любого символа а имеет место
(ха) =
(Pqa).

Теорема. Пусть

— функция конечного состояния автомата для поиска подстроки Р[1.. m]. Если Т[1.. n] — произвольный текст, то
i) =
i) для i=0,1,..., n. [14]

Из изложенного следует, что задача поиска подстроки состоит из двух частей:

построение автомата по образцу (определение функции переходов для заданного образца);

применение этого автомата для поиска вхождений образца в заданный текст.

1.5.2. Пример построения конечного автомата

Построим конечный автомат, допускающий строку ababaca. Поскольку длина образца m = 7 символов, то в автомате будет m + 1 = 8 состояний.

Найдем функцию переходов

. В соответствии с определением (1),
(q, a) =
qа), где
— префикс-функция, а — произвольный символ из алфавита
, q — номер состояния. Таким образом, необходимо для каждого префикса Pq = P[0..q], q = 0 .. m образца Р и для всех символов а входного алфавита
найти длину максимального префикса Р, который будет являться суффиксом строки Рqа. Длина этого префикса и будет значением функции переходов
(q,a). Если а = P[q + 1] (очередной символ текста совпал со следующим символом образца), то Рqа = Рq+1 и
(q, a) = q+1.