Алгоритм Бойера-Мура — различия между версиями
Kabanov (обсуждение | вклад) м |
Kabanov (обсуждение | вклад) (→Псевдо-код) |
||
Строка 51: | Строка 51: | ||
Функция для вычисления таблицы сдвигов плохих символов. Она будет равна длине шаблона для всех символов, которые не встречаются в шаблоне, и порядковому номеру с конца для остальных (кроме последнего, для него тоже берется длина шаблона). Вычисляется прямо по определению за <tex>O(m+\sigma)</tex> | Функция для вычисления таблицы сдвигов плохих символов. Она будет равна длине шаблона для всех символов, которые не встречаются в шаблоне, и порядковому номеру с конца для остальных (кроме последнего, для него тоже берется длина шаблона). Вычисляется прямо по определению за <tex>O(m+\sigma)</tex> | ||
− | int[] '''preBmBc'''(string x, int m): | + | '''int'''[] '''preBmBc'''('''string''' x, '''int''' m): |
− | int bmBc[ASIZE] | + | '''int''' bmBc[ASIZE] |
<font color=green>// Значение по умолчанию = m</font> | <font color=green>// Значение по умолчанию = m</font> | ||
− | for i = 0 .. ASIZE-1 | + | '''for''' i = 0 .. ASIZE-1 |
− | bmBc[i] = m | + | bmBc[i] = m |
− | for i = 0 .. m - 2 | + | '''for''' i = 0 .. m - 2 |
− | bmBc[x[i]] = m - i - 1 | + | bmBc[x[i]] = m - i - 1 |
− | return bmBc | + | '''return''' bmBc |
Функция для вычисления таблицы суффиксов. Она находит для каждой позиции в шаблоне <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 = 0 |
− | 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''' i > g and suff[i + m - 1 - f] < i - g | '''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''' | + | '''if''' i < g |
g = i | g = i | ||
f = i | f = i | ||
Строка 76: | Строка 76: | ||
--g | --g | ||
suff[i] = f - g | suff[i] = f - g | ||
− | return suff | + | '''return''' suff |
Функция для вычисления сдвигов хороших суффиксов | Функция для вычисления сдвигов хороших суффиксов | ||
− | void '''preBmGs'''(string x, int m): | + | '''void''' '''preBmGs'''(string x, int m): |
− | int i, j, suff[XSIZE] | + | '''int''' i, j, suff[XSIZE] |
− | int bmGs[] | + | '''int''' bmGs[] |
− | suff = suffixes(x, m) | + | suff = suffixes(x, m) |
− | for | + | '''for''' i = 0 .. m - 1 |
− | bmGs[i] = m | + | bmGs[i] = m |
− | j = 0 | + | j = 0 |
− | for i = m - 1 .. 0 | + | '''for''' i = m - 1 .. 0 |
− | if | + | '''if''' suff[i] == i + 1 |
− | while | + | '''while''' j < m - 1 - i |
− | if | + | '''if''' bmGs[j] == m |
− | bmGs[j] = m - 1 - i | + | bmGs[j] = m - 1 - i |
++j | ++j | ||
− | for i = 0 .. m - 2 | + | '''for''' i = 0 .. m - 2 |
− | bmGs[m - 1 - suff[i]] = m - 1 - i | + | bmGs[m - 1 - suff[i]] = m - 1 - i |
Основная функция алгоритма Бойера-Мура | Основная функция алгоритма Бойера-Мура | ||
− | void '''BM'''(string x, int m, string y, int n): | + | '''void''' '''BM'''(string x, int m, string y, int n): |
− | int bmGs[m] | + | '''int''' bmGs[m] |
− | int bmBc[ASIZE] | + | '''int''' bmBc[ASIZE] |
<font color=green>//Предварительные вычисления</font> | <font color=green>//Предварительные вычисления</font> | ||
− | bmGs = preBmGs(x, m) | + | bmGs = preBmGs(x, m) |
− | bmBc = preBmBc(x, m) | + | bmBc = preBmBc(x, m) |
<font color=green>//Поиск подстроки</font> | <font color=green>//Поиск подстроки</font> | ||
− | int j = 0 | + | '''int''' j = 0 |
− | while | + | '''while''' j <= n - m |
− | int i = m - 1 | + | '''int''' i = m - 1 |
− | while | + | '''while''' i >= 0 and x[i] == y[i + j] |
--i | --i | ||
− | if | + | '''if''' i < 0 |
− | OUTPUT(j) | + | OUTPUT(j) <font color=green>// Найдена подстрока в позиции j</font> |
− | j += bmGs[0] | + | j += bmGs[0] <font color=green>//Очевидно, что можем сделать сдвиг на период, т.к. там явно не будет совпадений.</font> |
− | else | + | '''else''' |
− | j += MAX(bmGs[i], bmBc[y[i + j]] - m + 1 + i) | + | j += MAX(bmGs[i], bmBc[y[i + j]] - m + 1 + i) |
==Асимптотики== | ==Асимптотики== |
Версия 18:27, 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 = 0 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 .. m - 1 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") скорость алгоритма Бойера-Мура серьёзно снижается.