Алгоритм Бойера-Мура — различия между версиями
Kabanov (обсуждение | вклад) м |
Kabanov (обсуждение | вклад) м |
||
| Строка 62: | Строка 62: | ||
Функция для вычисления таблицы суффиксов. Она находит для каждой позиции в шаблоне <tex>x</tex> максимальную длину суффикса <tex>x</tex>, который повторяется в строке и заканчивается в данной позиции. Например, для строки "'''abcabcabc'''" таблица будет '''0,0,3,0,0,6,0,0,9''', а для строки "'''abcabcc'''" - '''0,0,1,0,0,1,7'''. Также, очевидно, что значение функции для последнего элемента будет равно длине всей строки. | Функция для вычисления таблицы суффиксов. Она находит для каждой позиции в шаблоне <tex>x</tex> максимальную длину суффикса <tex>x</tex>, который повторяется в строке и заканчивается в данной позиции. Например, для строки "'''abcabcabc'''" таблица будет '''0,0,3,0,0,6,0,0,9''', а для строки "'''abcabcc'''" - '''0,0,1,0,0,1,7'''. Также, очевидно, что значение функции для последнего элемента будет равно длине всей строки. | ||
int[] '''suffixes'''(string x, int m): | int[] '''suffixes'''(string x, int m): | ||
| − | int f | + | int f |
| − | int suff[m] | + | int suff[m] |
| − | suff[m - 1] = m | + | suff[m - 1] = m |
| − | int g = m - 1 | + | int g = m - 1 |
| − | for i = m - 2 .. 0 | + | '''for''' i = m - 2 .. 0 |
| − | if | + | '''if''' i > g and suff[i + m - 1 - f] < i - g |
| − | suff[i] = suff[i + m - 1 - f] | + | suff[i] = suff[i + m - 1 - f] |
| − | else | + | '''else''' |
| − | if (i < g) | + | '''if''' (i < g) |
| − | g = i | + | g = i |
| − | f = i | + | f = i |
| − | while | + | '''while''' g >= 0 and x[g] == x[g + m - 1 - f] |
| − | --g | + | --g |
| − | suff[i] = f - g | + | suff[i] = f - g |
| − | return suff | + | return suff |
Функция для вычисления сдвигов хороших суффиксов | Функция для вычисления сдвигов хороших суффиксов | ||
| Строка 82: | Строка 82: | ||
int i, j, suff[XSIZE]; | int i, j, suff[XSIZE]; | ||
int bmGs[] | int bmGs[] | ||
| − | suff = suffixes(x, m); | + | suff = suffixes(x, m); |
| − | |||
for (i = 0; i < m; ++i) | for (i = 0; i < m; ++i) | ||
bmGs[i] = m; | bmGs[i] = m; | ||
| Строка 121: | Строка 120: | ||
* В худшем случае поиск требует <tex>O(m \cdot n)</tex> сравнений. | * В худшем случае поиск требует <tex>O(m \cdot n)</tex> сравнений. | ||
* В лучшем случае требует <tex>O(n / m)</tex> сравнений. | * В лучшем случае требует <tex>O(n / m)</tex> сравнений. | ||
| + | |||
| + | где <tex>n</tex> {{---}} длина исходного текста, <tex>m</tex> {{---}} длина шаблона, <tex>\sigma</tex> {{---}} размер алфавита. | ||
В 1991 году Р.Коул доказал следующую теорему: | В 1991 году Р.Коул доказал следующую теорему: | ||
| Строка 142: | Строка 143: | ||
==Ссылки== | ==Ссылки== | ||
| − | * [ | + | * [[wikipedia:ru:Алгоритм_Бойера_—_Мура|Википедия {{---}} Алгоритм Бойера-Мура]] |
| − | * [ | + | * [[wikipedia:ru:Алгоритм_Бойера_—_Мура_—_Хорспула|Википедия {{---}} Алгоритм Бойера-Мура-Хорспула]] |
| − | * [ | + | * [[wikipedia:Boyer–Moore_string_search_algorithm|Wikipedia {{---}} Boyer–Moore string search algorithm]] |
* [http://www-igm.univ-mlv.fr/~lecroq/string/node14.html#SECTION00140 Boyer-Moore algorithm] | * [http://www-igm.univ-mlv.fr/~lecroq/string/node14.html#SECTION00140 Boyer-Moore algorithm] | ||
[[Категория: Дискретная математика и алгоритмы]] | [[Категория: Дискретная математика и алгоритмы]] | ||
[[Категория: Поиск подстроки в строке]] | [[Категория: Поиск подстроки в строке]] | ||
Версия 18:18, 10 мая 2014
Алгоритм Бойера-Мура, разработанный двумя учеными — Бойером (Robert S. Boyer) и Муром (J. Strother Moore), считается наиболее быстрым среди алгоритмов общего назначения, предназначенных для поиска подстроки в строке. Важной особенностью алгоритма является то, что он выполняет сравнения в шаблоне справа налево в отличии от многих других алгоритмов.
Алгоритм Бойера-Мура считается наиболее эффективным алгоритмом поиска шаблонов в стандартных приложениях и командах, таких как Ctrl+F в браузерах и текстовых редакторах.
Содержание
Алгоритм
Алгоритм сравнивает символы шаблона справа налево, начиная с самого правого, один за другим с символами исходной строки . В случае несовпадения какого-либо символа (или полного совпадения всего шаблона) он использует две предварительно вычисляемых функций, чтобы сдвинуть позицию для начала сравнения вправо.
Пусть и .
Предположим, что в процессе сравнения возникает несовпадение между символом шаблона и символом исходного текста при проверке в позиции . Тогда и , т.е. символов паттерна уже совпало.
Операция сдвига хорошего суффикса состоит в выравнивании подстроки с её самым правым вхождением в , идущим справа от символа, отличного от .
Если не существует такого сегмента, то смещение состоит в выравнивании самого длинного суффикса подстроки с соответствующим префиксом .
Операция сдвига плохого символа состоит в выравнивании символа исходного текста с его самым правым появлением в .
Если не встречается в шаблоне x, то ни одно вхождение x в y не может включать в себя , и левый конец окна сравнения совмещен с символом непосредственно идущим после , т.е. .
Обратите внимание, что сдвиг плохого символа может быть отрицательным, таким образом для сдвига окна сравнения алгоритм Бойера-Мура использует значение, равное максимуму между сдвигом хорошего суффикса и сдвига плохого символа. Более формально две функции сдвигов определяются следующим образом:
Пусть значения функции сдвига хорошего суффикса хранятся в массиве размером .
Определим два условия:
- : для каждого такого, что выполняется или
- : если , то выполняется
Тогда для всех таких, что выполняется . А значение определим, как длину периода шаблона .
Для вычисления bmGs будем использовать массив , определенный так: для всех таких, что выполняется
Сдвиги плохих символов будем хранить в массиве размером . Для каждого символа из :
Массивы и вычисляются за времени до основной фазы поиска и требуют, очевидно, памяти.
Псевдо-код
Константой обозначим размер нашего алфавита.
Функция для вычисления таблицы сдвигов плохих символов. Она будет равна длине шаблона для всех символов, которые не встречаются в шаблоне, и порядковому номеру с конца для остальных (кроме последнего, для него тоже берется длина шаблона). Вычисляется прямо по определению за
int[] preBmBc(string x, int m):
int bmBc[ASIZE];
// Значение по умолчанию = m
for i = 0 .. ASIZE-1
bmBc[i] = m;
for i = 0 .. m - 2
bmBc[x[i]] = m - i - 1;
return bmBc;
Функция для вычисления таблицы суффиксов. Она находит для каждой позиции в шаблоне максимальную длину суффикса , который повторяется в строке и заканчивается в данной позиции. Например, для строки "abcabcabc" таблица будет 0,0,3,0,0,6,0,0,9, а для строки "abcabcc" - 0,0,1,0,0,1,7. Также, очевидно, что значение функции для последнего элемента будет равно длине всей строки.
int[] suffixes(string x, int m):
int f
int suff[m]
suff[m - 1] = m
int 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
return suff
Функция для вычисления сдвигов хороших суффиксов
void preBmGs(string x, int m):
int i, j, suff[XSIZE];
int bmGs[]
suff = suffixes(x, m);
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(string x, int m, string y, int n):
int bmGs[m];
int bmBc[ASIZE];
//Предварительные вычисления
bmGs = preBmGs(x, m);
bmBc = preBmBc(x, m);
//Поиск подстроки
int j = 0;
while (j <= n - m)
int i = m - 1;
while (i >= 0 and x[i] == y[i + j])
--i
if (i < 0)
OUTPUT(j); // Найдена подстрока в позиции j
j += bmGs[0]; //Очевидно, что можем сделать сдвиг на период, т.к. там явно не будет совпадений.
else
j += MAX(bmGs[i], bmBc[y[i + j]] - m + 1 + i);
Асимптотики
- Фаза предварительных вычислений требует времени и памяти
- В худшем случае поиск требует сравнений.
- В лучшем случае требует сравнений.
где — длина исходного текста, — длина шаблона, — размер алфавита.
В 1991 году Р.Коул доказал следующую теорему:
| Теорема (Richard Cole): |
В худшем случае требуется сравнений в случае шаблона с периодом равным длине самого шаблона. |
| Доказательство: |
| Доказательство [1] |
Сравнение с другими алгоритмами
Достоинства
- Алгоритм Бойера-Мура на хороших данных очень быстр, а вероятность появления плохих данных крайне мала. Поэтому он оптимален в большинстве случаев, когда нет возможности провести предварительную обработку текста, в котором проводится поиск.
- На больших алфавитах (относительно длины шаблона) алгоритм чрезвычайно быстрый и требует намного меньше памяти относительно алгоритма Ахо-Корасик.
- Алгоритм проще большинства алгоритмов поиска (при некоторых реализациях объем кода сравним с наивным поиском)
- Позволяет добавить множество модификаций, таких как поиск подстроки, включающей любой символ (?) (но для реализации множества символов (*) не походит, т.к. длина шаблона должна быть известна заранее).
Недостатки
- Алгоритмы семейства Бойера-Мура не расширяются до приблизительного поиска, поиска любой строки из нескольких.
- На больших алфавитах (например, Юникод) может занимать много памяти. В таких случаях либо обходятся хэш-таблицами, либо дробят алфавит, рассматривая, например, 4-байтовый символ как пару двухбайтовых.
- На искусственно подобранных неудачных текстах (например, шаблон "abcabcabcabcabc") скорость алгоритма Бойера-Мура серьёзно снижается.