Префикс-функция — различия между версиями
Neuner (обсуждение | вклад) |
|||
Строка 43: | Строка 43: | ||
Вносятся несколько важных замечаний: | Вносятся несколько важных замечаний: | ||
*Заметим, что <tex>\pi[i + 1] \leqslant \pi[i] + 1</tex>. Чтобы показать это, рассмотрим суффикс,оканчивающийся на позиции <tex>i + 1</tex> и имеющий длину <tex>\pi[i + 1]</tex>, удалив из него последний символ, мы получим суффикс, оканчивающийся на позиции <tex>i</tex> и имеющий длину <tex>\pi[i + 1] - 1</tex>, следовательно неравенство <tex>\pi[i + 1] > \pi[i] + 1</tex> неверно. | *Заметим, что <tex>\pi[i + 1] \leqslant \pi[i] + 1</tex>. Чтобы показать это, рассмотрим суффикс,оканчивающийся на позиции <tex>i + 1</tex> и имеющий длину <tex>\pi[i + 1]</tex>, удалив из него последний символ, мы получим суффикс, оканчивающийся на позиции <tex>i</tex> и имеющий длину <tex>\pi[i + 1] - 1</tex>, следовательно неравенство <tex>\pi[i + 1] > \pi[i] + 1</tex> неверно. | ||
− | *Избавимся от явных сравнений строк. | + | *Избавимся от явных сравнений строк. Пусть мы вычислили <tex>\pi[i]</tex>, тогда, если <tex>s[i + 1] = s[\pi[i]]</tex>, то <tex>\pi[i + 1] = \pi[i] + 1</tex>. Если окажется, что <tex>s[i + 1] \ne s[\pi[i]]</tex>, то нужно попытаться попробовать подстроку меньшей длины. Хотелось бы сразу перейти к такому [[Период_и_бордер,_их_связь#Определения|бордеру]] наибольшей длины, для этого подберем такое <tex>k</tex>, что <tex>k = \pi(i) - 1</tex>. Делаем это следующим образом. За исходное <tex>k</tex> необходимо взять <tex>\pi(i - 1)</tex>, что следует из первого пункта. В случае, когда символы <tex>s[k+1]</tex> и <tex>s[i]</tex> не совпадают, <tex>\pi(k)</tex> {{---}} следующее потенциальное наибольшее значение <tex>k</tex>, что видно из рисунка. Последнее утверждение верно, пока <tex>k>0</tex>, что позволит всегда найти его следующее значение. Если <tex>k=0</tex>, то <tex>\pi(i)=1</tex> при <tex>s[i] = s[1]</tex> , иначе <tex>\pi(i)=0</tex>. |
− | |||
− | |||
− | [[Файл: | + | [[Файл:Prfxx.png]] |
===Псевдокод=== | ===Псевдокод=== | ||
'''Prefix_function''' (<tex>s</tex>) | '''Prefix_function''' (<tex>s</tex>) | ||
Строка 95: | Строка 93: | ||
== Источники информации == | == Источники информации == | ||
+ | *[http://e-maxx.ru/algo/prefix_function Префикс-функция — e-maxx] | ||
+ | *[http://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D1%84%D0%B8%D0%BA%D1%81-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F Префикс-функция — Википедия] | ||
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. {{---}} 2-е изд. {{---}} М.: Издательский дом «Вильямс», 2007. {{---}} С. 1296 ISBN 978-5-8459-0857-5 | * Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. {{---}} 2-е изд. {{---}} М.: Издательский дом «Вильямс», 2007. {{---}} С. 1296 ISBN 978-5-8459-0857-5 | ||
− | |||
== См. также == | == См. также == | ||
+ | *[[Z-функция|Z-функция]] | ||
+ | *[[Алгоритм Кнута-Морриса-Пратта|Алгоритм Кнута-Морриса-Пратта]] | ||
+ | |||
[[Категория: Алгоритмы и структуры данных]] | [[Категория: Алгоритмы и структуры данных]] | ||
[[Категория: Поиск подстроки в строке]] | [[Категория: Поиск подстроки в строке]] |
Версия 18:37, 14 мая 2014
Определение: |
Префикс-функция (англ. prefix-function) от строки — длина наибольшего бордера этой строки |
Здесь и далее считаем, что символы в строках нумеруются с
.Определим префикс-функцию от строки
в позиции следующим образом: . Если мы не нашли такого , то .Содержание
Наивный алгоритм
Наивный алгоритм вычисляет префикс функцию непосредственно по определению, сравнивая префиксы и суффиксы строк. Обозначим длину строки за
Псевдокод
Prefix_function () fill( , 0) for i = 1 to n for k = 1 to i if s[1..k] == s[i - k + 1..i]) [i] = k return
Пример
Рассмотрим строку abcabcd, для которой значение префикс-функции равно
.Шаг | Строка | Значение функции |
---|---|---|
a | 0 | |
ab | 0 | |
abc | 0 | |
abca | 1 | |
abcab | 2 | |
abcabc | 3 | |
abcabcd | 0 |
Время работы
Всего
итераций цикла, на каждой из который происходит сравнение строк за , что дает в итоге .Эффективный алгоритм
Вносятся несколько важных замечаний:
- Заметим, что . Чтобы показать это, рассмотрим суффикс,оканчивающийся на позиции и имеющий длину , удалив из него последний символ, мы получим суффикс, оканчивающийся на позиции и имеющий длину , следовательно неравенство неверно.
- Избавимся от явных сравнений строк. Пусть мы вычислили бордеру наибольшей длины, для этого подберем такое , что . Делаем это следующим образом. За исходное необходимо взять , что следует из первого пункта. В случае, когда символы и не совпадают, — следующее потенциальное наибольшее значение , что видно из рисунка. Последнее утверждение верно, пока , что позволит всегда найти его следующее значение. Если , то при , иначе . , тогда, если , то . Если окажется, что , то нужно попытаться попробовать подстроку меньшей длины. Хотелось бы сразу перейти к такому
Псевдокод
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
Время работы
Время работы алгоритма составит
. Для доказательства этого нужно заметить, что итоговое количество итераций цикла определяет асимптотику алгоритма. Теперь стоит отметить, что увеличивается на каждом шаге не более чем на единицу, значит максимально возможное значение . Поскольку внутри цикла значение лишь уменьшается, получается, что не может суммарно уменьшиться больше, чем раз. Значит цикл в итоге выполнится не более раз, что дает итоговую оценку времени алгоритма .Построение строки по префикс-функции
Постановка задачи
Восстановить строку по префикс-функции за
, считая алфавит неограниченным.Описание алгоритма
Пусть в массиве
хранятся значения префикс-функции, в будет записан ответ. Пойдем по массиву слева направо.Пусть мы хотим узнать значение
. Для этого посмотрим на значение : если тогда в запишем новый символ, иначе . Обратим внимание, что нам уже известно, так как .Реализация
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
Доказательство корректности алгоритма
Докажем, что если нам дали корректную префикс-функцию, то наш алгоритм построит строку с такой же префикс-функцией. Также заметим, что строк с такой префикс-функцией может быть много, и алгоритм строит только одну из них.
Пусть
данная префикс-функция, правильная строка, строку построил наш алгоритм, массив значений префикс-функции для .Докажем корректность индукцией по длине массива префикс-функции полученной строки. Для начала заметим, что на предыдущие значения массива
прибавление нового символа не влияет, так как при подсчёте префикс-функции на -ой позиции рассматриваются символы на позициях не больше . Поэтому достаточно показать, что очередное значение префикс-функции будет вычислено правильно.- База очевидна для строки длины .
- Переход: пусть до
- . Тогда мы добавляем новый символ, поэтому тоже будет равно .
- бордера. Для этого будут использованы значения префикс-функции с меньшими индексами, которые посчитаны верно, опять же по предположению индукции. . По свойствам префикс-функции — суффикс и префикс строки длины продолжаются одним символом, значит, надо на текущую позицию строки поставить символ . Если значение префикс-функции увеличивается, значит, текущим символом продолжается префикс длины , а из свойств следует, что . По предположению индукцию значение будет вычислено верно. А если значение префикс-функции не увеличивается, значит, символ должен продолжить префикс меньшей длины, а в текущее значение префикс-функции запишется как раз длина нового
-ой позиции мы построили строку, что . Возможны два случая:
Источники информации
- Префикс-функция — e-maxx
- Префикс-функция — Википедия
- Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296 ISBN 978-5-8459-0857-5