Префикс-функция — различия между версиями
Sergej (обсуждение | вклад) (→Псевдокод) |
Sergej (обсуждение | вклад) (→Псевдокод) |
||
Строка 102: | Строка 102: | ||
=== Псевдокод === | === Псевдокод === | ||
'''int'''[] buildPrefixFunctionFromZFunction('''int'''[] z) | '''int'''[] buildPrefixFunctionFromZFunction('''int'''[] z) | ||
− | + | '''int'''[] p = '''new int'''[n] | |
− | + | '''for''' i = 0 '''to''' p.length - 1 | |
− | + | '''if''' z[i] > 0 | |
− | + | '''for''' j = z[i] - 1 '''downto''' 0 | |
− | + | '''if''' p[i + j] > 0 break | |
− | + | '''else''' p[i + j] = j + 1 | |
== См. также == | == См. также == |
Версия 18:08, 18 мая 2014
Определение: |
Префикс-функция (англ. prefix-function) от строки — массив длин наибольших бордеров для каждой позиции этой строки |
Здесь и далее считаем, что символы в строках нумеруются с
.Определим префикс-функцию от строки
в позиции следующим образом: . Если мы не нашли такого , то .Содержание
Наивный алгоритм
Наивный алгоритм вычисляет префикс функцию непосредственно по определению, сравнивая префиксы и суффиксы строк. Обозначим длину строки за
.Псевдокод
int[] prefixFunction(string s) fill(, 0) for i = 1 to n for k = 1 to i if s[1..k] == s[i - k + 1..i] [i] = k return
Пример
Рассмотрим строку
, для которой значение префикс-функции равно .Шаг | Строка | Значение функции |
---|---|---|
a | 0 | |
ab | 0 | |
abc | 0 | |
abca | 1 | |
abcab | 2 | |
abcabc | 3 | |
abcabcd | 0 |
Время работы
Всего
итераций цикла, на каждой из который происходит сравнение строк за , что дает в итоге .Эффективный алгоритм
Вносятся несколько важных замечаний:
- Заметим, что . Чтобы показать это, рассмотрим суффикс,оканчивающийся на позиции и имеющий длину , удалив из него последний символ, мы получим суффикс, оканчивающийся на позиции и имеющий длину , следовательно неравенство неверно.
- Избавимся от явных сравнений строк. Пусть мы вычислили бордеру наибольшей длины, для этого подберем такое , что . Делаем это следующим образом. За исходное необходимо взять , что следует из первого пункта. В случае, когда символы и не совпадают, — следующее потенциальное наибольшее значение , что видно из рисунка. Последнее утверждение верно, пока , что позволит всегда найти его следующее значение. Если , то при , иначе . , тогда, если , то . Если окажется, что , то нужно попытаться попробовать подстроку меньшей длины. Хотелось бы сразу перейти к такому
Псевдокод
int[] prefixFunction(string s)[1] = 0 for i = 2 to n k = [i-1] while k > 0 and s[i] != s[k + 1] k = [k] if s[i] == s[k + 1] k++ [i] = k return
Время работы
Время работы алгоритма составит
. Для доказательства этого нужно заметить, что итоговое количество итераций цикла определяет асимптотику алгоритма. Теперь стоит отметить, что увеличивается на каждом шаге не более чем на единицу, значит максимально возможное значение . Поскольку внутри цикла значение лишь уменьшается, получается, что не может суммарно уменьшиться больше, чем раз. Значит цикл в итоге выполнится не более раз, что дает итоговую оценку времени алгоритма .Построение строки по префикс-функции
Постановка задачи
Восстановить строку по префикс-функции за
, считая алфавит неограниченным.Описание алгоритма
Пусть в массиве
хранятся значения префикс-функции, в будет записан ответ. Пойдем по массиву слева направо.Пусть мы хотим узнать значение
. Для этого посмотрим на значение : если тогда в запишем новый символ, иначе . Обратим внимание, что нам уже известно, так как .Реализация
string buildFromPrefix(int[] p): s = "" for i = 0 to p.length - 1 if p[i] == 0 s += new character else s += s[p[i]] return s
Доказательство корректности алгоритма
Докажем, что если нам дали корректную префикс-функцию, то наш алгоритм построит строку с такой же префикс-функцией. Также заметим, что строк с такой префикс-функцией может быть много, и алгоритм строит только одну из них.
Пусть
данная префикс-функция, правильная строка, строку построил наш алгоритм, массив значений префикс-функции для .Докажем корректность индукцией по длине массива префикс-функции полученной строки. Для начала заметим, что на предыдущие значения массива
прибавление нового символа не влияет, так как при подсчёте префикс-функции на -ой позиции рассматриваются символы на позициях не больше . Поэтому достаточно показать, что очередное значение префикс-функции будет вычислено правильно.- База очевидна для строки длины .
- Переход: пусть до
- . Тогда мы добавляем новый символ, поэтому тоже будет равно .
- бордера. Для этого будут использованы значения префикс-функции с меньшими индексами, которые посчитаны верно, опять же по предположению индукции. . По свойствам префикс-функции — суффикс и префикс строки длины продолжаются одним символом, значит, надо на текущую позицию строки поставить символ . Если значение префикс-функции увеличивается, значит, текущим символом продолжается префикс длины , а из свойств следует, что . По предположению индукцию значение будет вычислено верно. А если значение префикс-функции не увеличивается, значит, символ должен продолжить префикс меньшей длины, а в текущее значение префикс-функции запишется как раз длина нового
-ой позиции мы построили строку, что . Возможны два случая:
Построение префикс-функции по функции
Постановка задачи
Дан массив с корректной
функцией для строки , получить за массив с префикс-функцией для строкиОписание алгоритма
Пусть
функция хранится в массиве префикс-функцию будем записывать в массив . Пойдем по массиву слева направо, заметим, что если то для всех элементов с индексом , где значение будет не меньше, чем в силу определения префикс и функции. Также заметим, что если мы уже установили значение в какое-то то менять уже не нужно, так как это только уменьшит значение В итоге получаем алгоритм, идем слева направо по массиву . Пусть мы сейчас находимся на позиции тогда пытаемся записать в от позиции до значение где пробегает все значения В итоге каждый элемент в будет просмотрен не более двух раз, откуда получаем асимптотикуПсевдокод
int[] buildPrefixFunctionFromZFunction(int[] z) int[] p = new int[n] for i = 0 to p.length - 1 if z[i] > 0 for j = z[i] - 1 downto 0 if p[i + j] > 0 break else p[i + j] = j + 1
См. также
Источники информации
- Википедия — Префикс-функция
- MAXimal :: algo — Префикс-функция
- Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296 ISBN 978-5-8459-0857-5