Префикс-функция — различия между версиями
Vasin (обсуждение | вклад) (→Псевдокод) |
Vasin (обсуждение | вклад) (→Оптимизация) |
||
Строка 39: | Строка 39: | ||
*<tex>\pi(i)</tex> превосходит <tex>\pi(i-1)</tex> не больше, чем на <tex>1</tex>. Действительно, если <tex>\pi(i) > \pi(i-1) + 1</tex>, тогда <tex>\pi(i) - 1 > \pi(i-1)</tex>, значит в <tex>\pi(i-1)</tex> не максимально возможное значение, получили противоречие. | *<tex>\pi(i)</tex> превосходит <tex>\pi(i-1)</tex> не больше, чем на <tex>1</tex>. Действительно, если <tex>\pi(i) > \pi(i-1) + 1</tex>, тогда <tex>\pi(i) - 1 > \pi(i-1)</tex>, значит в <tex>\pi(i-1)</tex> не максимально возможное значение, получили противоречие. | ||
*Избавимся от явных сравнений строк. Пусть мы вычислили <tex>\pi(i-1)</tex> и <tex>s[\pi(i-1) + 1] = s[i]</tex>, тогда очевидно <tex>\pi(i) = \pi(i-1) + 1</tex>. Если же условие <tex>s[\pi(i) + 1] = s[i + 1]</tex> ложно, то хотелось бы найти наибольшую длину <tex> k</tex>, для которой верно <tex>\pi(i) = k + 1</tex>. Когда мы найдем такое <tex>k</tex> нам достаточно будет сравнить <tex>s[k + 1]</tex> и <tex>s[i]</tex>, при их равенстве <tex>\pi(i) = k + 1</tex> будет верно. Будем искать наше <tex>k</tex> пока оно больше нуля, при равенстве нулю <tex>\pi(i) = 1</tex>, если <tex>s[i] = s[1]</tex>, иначе нулю. Общая схема алгоритма у нас есть, теперь нужно только научиться искать <tex>k</tex>. | *Избавимся от явных сравнений строк. Пусть мы вычислили <tex>\pi(i-1)</tex> и <tex>s[\pi(i-1) + 1] = s[i]</tex>, тогда очевидно <tex>\pi(i) = \pi(i-1) + 1</tex>. Если же условие <tex>s[\pi(i) + 1] = s[i + 1]</tex> ложно, то хотелось бы найти наибольшую длину <tex> k</tex>, для которой верно <tex>\pi(i) = k + 1</tex>. Когда мы найдем такое <tex>k</tex> нам достаточно будет сравнить <tex>s[k + 1]</tex> и <tex>s[i]</tex>, при их равенстве <tex>\pi(i) = k + 1</tex> будет верно. Будем искать наше <tex>k</tex> пока оно больше нуля, при равенстве нулю <tex>\pi(i) = 1</tex>, если <tex>s[i] = s[1]</tex>, иначе нулю. Общая схема алгоритма у нас есть, теперь нужно только научиться искать <tex>k</tex>. | ||
− | *Очевидно, что за исходное <tex>k</tex> нужно взять <tex>\pi(k - 1)</tex>. Как видно из рисунка, приведенного ниже, при совпадении символов <tex>s[k + 1]</tex> и <tex>s[i]</tex> длина наибольшего общего префикса увеличивается на единицу. В случае, когда символы <tex>s[k+1]</tex> и <tex>s[i]</tex> не совпадают, <tex>\pi(k)</tex> {{---}} следующая по максимальности длина потенциального наибольшего общего префикса, что тоже понятно из рисунка. | + | *Очевидно, что за исходное <tex>k</tex> нужно взять <tex>\pi(k - 1)</tex>. Как видно из рисунка, приведенного ниже, при совпадении символов <tex>s[k + 1]</tex> и <tex>s[i]</tex> длина наибольшего общего префикса увеличивается на единицу. В случае, когда символы <tex>s[k+1]</tex> и <tex>s[i]</tex> не совпадают, <tex>\pi(k)</tex> {{---}} следующая по максимальности длина потенциального наибольшего общего префикса, что тоже понятно из рисунка. Последнее утверждение продолжается по индукции, и получается требуемый поиск <tex>k</tex> |
[[Файл:Prefix2.jpg]] | [[Файл:Prefix2.jpg]] |
Версия 22:20, 8 июня 2012
Префикс-функция строки
— функция .Содержание
Алгоритм
Наивный алгоритм вычисляет префикс функцию непосредственно по определению, сравнивая префиксы и суффиксы строк.
Пример
Рассмотрим строку abcabcd, для которой значение префикс-функции равно
.Шаг | Строка | Значение функции |
---|---|---|
a | 0 | |
ab | 0 | |
abc | 0 | |
abca | 1 | |
abcab | 2 | |
abcabc | 3 | |
abcabcd | 0 |
Псевдокод
Prefix_function () = [] for i = 1 to n for k = 1 to i - 1 if s[1..k] == s[i - k + 1..i] [i] = k return
Время работы
Всего
итераций цикла, на каждой из который происходит сравнение строк за , что дает в итоге .Оптимизация
Внесем несколько важных замечаний:
- превосходит не больше, чем на . Действительно, если , тогда , значит в не максимально возможное значение, получили противоречие.
- Избавимся от явных сравнений строк. Пусть мы вычислили и , тогда очевидно . Если же условие ложно, то хотелось бы найти наибольшую длину , для которой верно . Когда мы найдем такое нам достаточно будет сравнить и , при их равенстве будет верно. Будем искать наше пока оно больше нуля, при равенстве нулю , если , иначе нулю. Общая схема алгоритма у нас есть, теперь нужно только научиться искать .
- Очевидно, что за исходное нужно взять . Как видно из рисунка, приведенного ниже, при совпадении символов и длина наибольшего общего префикса увеличивается на единицу. В случае, когда символы и не совпадают, — следующая по максимальности длина потенциального наибольшего общего префикса, что тоже понятно из рисунка. Последнее утверждение продолжается по индукции, и получается требуемый поиск
Псевдокод
Prefix_function () [1] = 0 for i = 2 to n k = [i - 1] while k > 0 && s[i] != s[k + 1] k = [k] if s[i] == s[k + 1] k++ [i] = k return
Время работы
С помощью метода потенциалов можно показать, что время работы
. Потенциал величины связывается с текущим ее значением в алгоритме. Начальное значение этого потенциала равно нулю. На каждой итерации цикла значение уменьшается, поскольку . Однако в силу значение этой переменной не бывает отрицательным. Также значение изменяется не более чем на 1 внутри тела цикла . Поскольку перед входом в цикл выполняется и поскольку значение переменной увеличивается в каждой итерации цикла , справедливость неравенства сохраняется (подтверждая тот факт, что соблюдается также неравенство . Каждое выполнение тела цикла можно оплатить соответствующим уменьшение потенциальной функции, поскольку . Кроме этого значение потенциальной функции возрастает не более чем на 1, из-за этого амортизированная стоимость тела цикла — . Так как количество итераций , и поскольку конечное значение потенциальной функции по величине не меньше, чем ее начальное значение, полное время работы в наихудшем случае равно .Литература
Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.