Поиск наибольшей общей подстроки двух строк с использованием хеширования
Постановка задачи
Имеются строки
и такие, что элементы этих строк символы из конечного алфавита . Говорят, что строка [1 .. m] является подстрокой строки [1 .. n], если существует такой индекс ∈ [0 .. n - m], что для любого ∈ [1 .. m] справедливо . Требуется найти такую строку , максимальной длины, что является и подстрокой , и подстрокой .Алгоритм
Данный алгоритм основывается на методе половинного деления. Пусть длина наибольшей общей подстроки будет алгоритме Рабина-Карпа.
. Заметим, что у строк и обязательно найдется общая подстрока длины ∈ [0 .. ], так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию : [0 .. min(len( ), len( ))] → {0, 1}, которая для из области определения равна 1, если у строк и есть общая подстрока длины , иначе она равна 0. Согласно замечанию, функция должна по мере возрастания быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом требуется с помощью бинарного поиска найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. При этом предполагается использовать хеширование, чтобы улучшить асимптотику алгоритма. Делается это следующим образом: 1) у строки хешируем подстроки заданной длины и полученные хеши записываем в Set. 2) у строки хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set выполняем посимвольную проверку на совпадение подстрок. Предполагается, что хеширование будет проводится так же, как и вПсевдокод
int f(int) - функция описанная в алгоритме
int findGCS(S, T) n = min(len(S), len(T)) left = 0 right = n + 1 while (right - left > 1): val = (left + right) / 2 if (f(val) == 1) left = val else right = val return left
Время работы
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки этого метода, то это занимает линейное время. Значит,на каждый шаг бинарного поиска требуется O(max(len( ), len( ))) действий. На самом деле требуется несколько больше времени, поскольку совпадение хешей не дает гарантии совпадения подстрок, однако чтобы это было справедливо с большой вероятностью, достаточно проверить совпадение лишь нескольких произвольных символов, вместо полной проверки. Тогда на это потребуется некоторое константное число операций, что маскируется с помощью O. Заметим, что всего для завершения бинарного поиска потребуется O(log(min(len( ), len( )))) шагов. Следовательно, суммарное время работы алгоритма будет O(log(min(len( ), len( ))) * max(len( ), len( ))) действий.
и запись их в Set требует O(len( )) шагов. Во-вторых, хеширование подстрок строки и проверка их наличия в Set требует O(len( )). В приведенных рассуждениях предполагается, что операции записи в Set и проверка наличия элемента в Set работают за амортизированную O(1). Поскольку хешировали с помощьюЛитература
Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.