Алгоритм Бойера-Мура — различия между версиями
Kabanov (обсуждение | вклад) м (→Алгоритм) |
Kabanov (обсуждение | вклад) |
||
Строка 124: | Строка 124: | ||
===Достоинства=== | ===Достоинства=== | ||
* Алгоритм Бойера-Мура на хороших данных очень быстр, а вероятность появления плохих данных крайне мала. Поэтому он оптимален в большинстве случаев, когда нет возможности провести предварительную обработку текста, в котором проводится поиск. | * Алгоритм Бойера-Мура на хороших данных очень быстр, а вероятность появления плохих данных крайне мала. Поэтому он оптимален в большинстве случаев, когда нет возможности провести предварительную обработку текста, в котором проводится поиск. | ||
+ | * На больших алфавитах (относительно длины шаблона) алгоритм чрезвычайно быстрый и требует намного меньше памяти относительно [[Алгоритм Ахо-Корасик|алгоритма Ахо-Корасик]]. | ||
* Алгоритм проще большинства алгоритмов поиска (при некоторых реализациях объем кода сравним с наивным поиском) | * Алгоритм проще большинства алгоритмов поиска (при некоторых реализациях объем кода сравним с наивным поиском) | ||
* Позволяет добавить множество модификаций, таких как поиск подстроки, включающей ''любой символ (?)'' (но для реализации ''множества символов (*)'' не походит, т.к. длина шаблона должна быть известна заранее). | * Позволяет добавить множество модификаций, таких как поиск подстроки, включающей ''любой символ (?)'' (но для реализации ''множества символов (*)'' не походит, т.к. длина шаблона должна быть известна заранее). | ||
Строка 136: | Строка 137: | ||
* [http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D0%B9%D0%B5%D1%80%D0%B0_%E2%80%94_%D0%9C%D1%83%D1%80%D0%B0_%E2%80%94_%D0%A5%D0%BE%D1%80%D1%81%D0%BF%D1%83%D0%BB%D0%B0 Википедия:Алгоритм Бойера-Мура-Хорспула] | * [http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D0%B9%D0%B5%D1%80%D0%B0_%E2%80%94_%D0%9C%D1%83%D1%80%D0%B0_%E2%80%94_%D0%A5%D0%BE%D1%80%D1%81%D0%BF%D1%83%D0%BB%D0%B0 Википедия:Алгоритм Бойера-Мура-Хорспула] | ||
* [http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm Wikipedia:Boyer–Moore string search algorithm] | * [http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm Wikipedia:Boyer–Moore string search algorithm] | ||
− | * [http://www-igm.univ-mlv.fr/~lecroq/string/node14.html#SECTION00140] | + | * [http://www-igm.univ-mlv.fr/~lecroq/string/node14.html#SECTION00140 Boyer-Moore algorithm] |
Версия 00:45, 10 мая 2014
Алгоритм Бойера-Мура, разработанный двумя учеными – Бойером (Robert S. Boyer) и Муром (J. Strother Moore), считается наиболее быстрым среди алгоритмов общего назначения, предназначенных для поиска подстроки в строке. Важной особенностью алгоритма является то, что он выполняет сравнения справа налево в отличии от многих других алгоритмов.
Алгоритм Бойера-Мура считается наиболее эффективным алгоритмом поиска шаблонов в стандартных приложениях и командах, таких как Ctrl+F в браузерах и текстовых редакторах.
Содержание
Асимптотики
- Фаза предварительных вычислений требует времени и памяти
- В худшем случае поиск требует сравнений.
- В лучшем случае требует сравнений.
В 1991 году Р.Коул доказал следующую теорему:
Теорема (Richard Cole): |
В худшем случае требуется сравнений в случае шаблона с периодом равным длине самого шаблона. |
Доказательство: |
Доказательство [1] |
Алгоритм
Алгоритм сравнивает символы шаблона (
) справа налево, начиная с самого правого, один за другим с символами исходной строки ( ). В случае несовпадения какого-либо символа (или полного совпадения всего шаблона) он использует две предварительно вычисляемых функций, чтобы сдвинуть позицию для начала сравнения вправо.Пусть
и .Предположим, что в процессе сравнения возникает несовпадение между символом
шаблона и символом исходного текста при проверке в позиции . Тогда и , т.е. символов паттерна уже совпало.Операция сдвига хорошего суффикса состоит в выравнивании подстроки
с её самым правым вхождением в , идущим справа от символа, отличного от .Если не существует такого сегмента, то смещение состоит в выравнивании самого длинного суффикса
подстроки с соответствующим префиксом .Операция сдвига плохого символа состоит в выравнивании символа исходного текста
с его самым правым появлением в .Если
не встречается в шаблоне x, то ни одно вхождение x в y не может включать в себя , и левый конец окна сравнения совмещен с символом непосредственно идущим после , т.е. .Обратите внимание, что сдвиг плохого символ может быть отрицательным, таким образом для сдвига окна сравнения алгоритм Бойера-Мура использует значение, равное максимуму между сдвигом хорошего суффикса и сдвига плохого символа. Более формально две функции сдвигов определяются следующим образом:
Пусть значения функции сдвига хорошего суффикса хранятся в таблице
размером .Определим два условия:
- : для каждого такого, что выполняется или
- : если , то выполняется
Тогда для всех
таких, что выполняется . А значение определим, как длину периода шаблона .Для вычисления bmGs будем использовать таблицу
, определенную так: для всех таких, что выполняетсяСдвиги плохих символов будем хранить в массиве
размером . Для каждого символа из :Массивы
и вычисляются за времени до основной фазы поиска и требуют, очевидно, памяти.Псевдо-код
Функция для вычисления таблицы сдвигов плохих символов
void preBmBc(string x, int m, int bmBc[]): for i = 0 .. ASIZE-1 bmBc[i] = m for i = 0 .. m - 2 bmBc[x[i]] = m - i - 1
Функция для вычисления таблицы суффиксов
void suffixes(string x, int m, int *suff): int f, g, i; suff[m - 1] = m; g = m - 1; for i = m - 2 .. 0 if (i > g and suff[i + m - 1 - f] < i - g) suff[i] = suff[i + m - 1 - f]; else if (i < g) g = i; f = i; while (g >= 0 and x[g] == x[g + m - 1 - f]) --g; suff[i] = f - g;
Функция для вычисления сдвигов хороших суффиксов
void preBmGs(string x, int m, int bmGs[]): int i, j, suff[XSIZE]; suffixes(x, m, suff); for (i = 0; i < m; ++i) bmGs[i] = m; j = 0; for i = m - 1 .. 0 if (suff[i] == i + 1) while (j < m - 1 - i) if (bmGs[j] == m) bmGs[j] = m - 1 - i; ++j for i = 0 .. m - 2 bmGs[m - 1 - suff[i]] = m - 1 - i;
Основная функция алгоритма Бойера-Мура
void BM(char *x, int m, char *y, int n): int i, j, bmGs[XSIZE], bmBc[ASIZE]; //Предварительные вычисления preBmGs(x, m, bmGs); preBmBc(x, m, bmBc); //Поиск подстроки j = 0; while (j <= n - m) i = m - 1 while (i >= 0 and x[i] == y[i + j]) --i if (i < 0) OUTPUT(j); j += bmGs[0]; else j += MAX(bmGs[i], bmBc[y[i + j]] - m + 1 + i);
Сравнение с другими алгоритмами
Достоинства
- Алгоритм Бойера-Мура на хороших данных очень быстр, а вероятность появления плохих данных крайне мала. Поэтому он оптимален в большинстве случаев, когда нет возможности провести предварительную обработку текста, в котором проводится поиск.
- На больших алфавитах (относительно длины шаблона) алгоритм чрезвычайно быстрый и требует намного меньше памяти относительно алгоритма Ахо-Корасик.
- Алгоритм проще большинства алгоритмов поиска (при некоторых реализациях объем кода сравним с наивным поиском)
- Позволяет добавить множество модификаций, таких как поиск подстроки, включающей любой символ (?) (но для реализации множества символов (*) не походит, т.к. длина шаблона должна быть известна заранее).
Недостатки
- Алгоритмы семейства Бойера-Мура не расширяются до приблизительного поиска, поиска любой строки из нескольких.
- Сравнение не является "чёрным ящиком", поэтому при реализации наиболее быстрого поиска приходится либо рассчитывать на удачную работу оптимизатора, либо вручную оптимизировать поиск.
- На больших алфавитах (например, Юникод) может занимать много памяти. В таких случаях либо обходятся хэш-таблицами, либо дробят алфавит, рассматривая, например, 4-байтовый символ как пару двухбайтовых.
- На искусственно подобранных неудачных текстах (например, needle=«колоколоколоколоколокол») скорость алгоритма Бойера-Мура серьёзно снижается.