Алгоритм поиска строки Бойера — Мура — алгоритм общего назначения, предназначенный для поиска подстроки в строке. Давайте попробуем найти вхождение подстроки в строку. Наш исходный текст будет: И паттерн, который мы будем искать Давайте расставим индексы в нашем тексте, чтобы видеть на каком индексе находится какая буква. Напишем метод, который определяет, находится ли шаблон в строке. Метод будет возвращать индекс откуда шаблон входит в строку. В нашем примере должно будет вернуться 4. Если ответ не найден, мы будем возвращать -1 Создадим цикл для прохода по тексту. Почему именно такое условие? Потому что если останется символов меньше чем длина нашего паттерна, нам уже нет смысла проверять дальше. Наш паттерн точно не входит в строку. В нашем примере: last = 5 (Последний индекс паттерна string равен 5) Длина текста равна 10 (somestring) Это значит что text.length — last = 10–5 = 5; Можно заметить, что если мы стоим на 5м индексе в строке, а 4й не подошел, то наш паттерн не входит в строку, например Получится что длина паттерна, больше чем длина оставшейся строки. На данный момент у нас есть код вида : Теперь введем переменную int p = 0, которая будет двигаться по нашему паттерну. И запустим внутренний цикл p <= last — пока меньше или равно последнему индексу символа шаблона text[ t + p ] = pattern[p] — пока очередной сивмол текста совпадает с символом в шаблоне. Давайте детальнее разберем, что значит text[ t + p ] = pattern[p] Допустим мы в тексте стоим на индексе 4 Получается t = 4, p = 0; text[ t + p ] = text[ 4 + 0 ] = s, pattern[0] = s, значит условие цикла выполняется. Код на текущий момент: Получается мы имеем такой метод: Представим что у нас в тексте есть символ, которого нет в паттерне. text: some*string , то на сколько можно сдвигать паттерн? А если есть повторяющиеся символы? Например можно начать бежать с конца паттерна, то есть Начинаем бежать с конца паттерна. Если символы не совпадают, на сколько можно сдвинуть паттерн? Если символ из текста есть в нашем паттерне, то можно сразу сдвинуть до этого символа. Сдвигаем на длину паттерна минус индекс символа на котором мы стоим в тексте. То есть в данном случае Когда мы начинаем бежать по паттерну с конца у нас pattern[i] = ‘g’, а text[i] = ’t’, мы можем сдвинуть наш паттерн на pattern.length — индекс ’t’ в паттерне. Индекс ’t’ в паттерне = 1, получается 5–1 = 4, сдвигаем паттерн на 4 символа вперед. Мы можем предварительно создать таблицу для нашего паттерна. Посчитаем позицию каждого символа в паттерне, чтобы знать на сколько сдвигаться. Составляем таблицу смещений, для каждого символа алфавита. Я буду в примере использовать символ * чтобы не расписывать весь алфавит. Давайте возьмем другой паттерн, чтобы символы повторялись и было ясно, на сколько нужно двигать паттерн, если символы повторяются: Мы сдвигаем на последние вхождение символа, потому что если сдвигать на первое, то можно упустить часть входа паттерна в строку. Попробуем реализовать эту часть алгоритма: Добавим таблицу смещений в алгоритм поиска, что мы написали выше: Почему t + last ? Смотрим на каком символе стоим в тексте и прибавляем длину шаблона. Если при поиске входа, какая то часть не совпала, то мы должны сдвинуться на символ текста в котором стоим + длина шаблона. Например: Шаг 1: Шаг 2: Шаг 3: Шаг 4: Шаг 5: Совпадение найдено. На этом разбор упрощенного алгоритма Бойера-Мура закончен. Пока мы отталкивались только от крайних символов. Но возможно ли как то использовать информацию по уже пройденным символам? Допустим есть строка abcdadcd Как составить таблицу смещений мы уже знаем. Теперь составим таблицу суффиксов. Когда мы стоим на символе d, на сколько можно сдвинуть паттерн? Сдвигаем на на расстояние до ближайшего такого же символа. В примере ниже у нас есть 2 символа d с расстояниями 2 и 4. Мы выберем 2 потому что оно меньше. Дальше рассмотрим суффикс cd. Для d мы уже записали 2. И для всех остальных символов пишем 8, потому что суффиксы не будут совпадать. Теперь вернемся к нашему паттерну колокол. колокол* В конце всегда будет *, еcли символ не совпал, сдвинем на 1. Для окол мы тоже запишем 4. Потому что у нас префикс совпадает с суффиксом. Попробуем написать код для этого Например колокол с чем сравниваем? Существует более оптимальный алгоритм, но дальше индивидуально. Какой код у нас есть на данный момент? p — prefix[text[t + p]] — последний символ, который нашли и под него подстроить сдвиг нашего шаблона suffix[p + 1] — значение суффикса для последнего элемента, который был сравнен. И двигаем на максимальное значение, чтобы двигаться максимально быстро. На этом разбор алгоритма Бойера-Мура закончен. Спасибо за внимание! =) Software Engineer