Префикс-функция — различия между версиями
Neuner (обсуждение | вклад) |
Neuner (обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
{{Определение | {{Определение | ||
− | |definition = '''Префикс-функция''' ''(англ. prefix-function)'' от строки {{---}} | + | |definition = '''Префикс-функция''' ''(англ. prefix-function)'' от строки {{---}} массив длин наибольших [[Период_и_бордер,_их_связь#Определения|бордеров]] для каждой позиции этой строки}} |
Здесь и далее считаем, что символы в строках нумеруются с <tex>1</tex>. | Здесь и далее считаем, что символы в строках нумеруются с <tex>1</tex>. | ||
Строка 9: | Строка 9: | ||
===Псевдокод=== | ===Псевдокод=== | ||
− | ''' | + | '''int'''[] Prefix_function(string s) |
fill(<tex>\pi</tex>, 0) | fill(<tex>\pi</tex>, 0) | ||
'''for''' i = 1 '''to''' n | '''for''' i = 1 '''to''' n | ||
'''for''' k = 1 '''to''' i | '''for''' k = 1 '''to''' i | ||
− | '''if''' s[1..k] == s[i - k + 1..i] | + | '''if''' s[1..k] == s[i - k + 1..i] |
<tex>\pi</tex>[i] = k | <tex>\pi</tex>[i] = k | ||
'''return''' <tex>\pi</tex> | '''return''' <tex>\pi</tex> | ||
Строка 45: | Строка 45: | ||
*Избавимся от явных сравнений строк. Пусть мы вычислили <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>. | *Избавимся от явных сравнений строк. Пусть мы вычислили <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>. | ||
− | [[Файл: | + | [[Файл:Untitled-1.jpg|800px]] |
===Псевдокод=== | ===Псевдокод=== | ||
− | ''' | + | '''int'''[] Prefix_function(string s) |
<tex>\pi</tex>[1] = 0 | <tex>\pi</tex>[1] = 0 | ||
'''for''' i = 2 '''to''' n | '''for''' i = 2 '''to''' n | ||
k = <tex>\pi</tex>[i-1] | k = <tex>\pi</tex>[i-1] | ||
− | '''while''' k > 0 | + | '''while''' k > 0 '''and''' s[i] != s[k + 1] |
k = <tex>\pi</tex>[k] | k = <tex>\pi</tex>[k] | ||
'''if''' s[i] == s[k + 1] | '''if''' s[i] == s[k + 1] | ||
Строка 91: | Строка 91: | ||
** <tex>p[n] = 0</tex>. Тогда мы добавляем новый символ, поэтому <tex>q[n]</tex> тоже будет равно <tex>0</tex>. | ** <tex>p[n] = 0</tex>. Тогда мы добавляем новый символ, поэтому <tex>q[n]</tex> тоже будет равно <tex>0</tex>. | ||
** <tex>p[n] > 0</tex>. По свойствам префикс-функции <tex> s'[p[n]] = s'[n] </tex> {{---}} суффикс и префикс строки <tex> s' </tex> длины <tex> p[n] </tex> продолжаются одним символом, значит, надо на текущую позицию строки <tex> s </tex> поставить символ <tex> s[p[n]] </tex>. Если значение префикс-функции увеличивается, значит, текущим символом продолжается префикс длины <tex> p[n - 1] </tex>, а из свойств следует, что <tex> p[n - 1] \geqslant p[n] - 1 </tex>. По предположению индукцию значение <tex> q[n - 1] </tex> будет вычислено верно. А если значение префикс-функции не увеличивается, значит, символ <tex> s[n] </tex> должен продолжить префикс меньшей длины, а в текущее значение префикс-функции запишется как раз длина нового [[Период_и_бордер,_их_связь#Определения|бордера]]. Для этого будут использованы значения префикс-функции с меньшими индексами, которые посчитаны верно, опять же по предположению индукции. | ** <tex>p[n] > 0</tex>. По свойствам префикс-функции <tex> s'[p[n]] = s'[n] </tex> {{---}} суффикс и префикс строки <tex> s' </tex> длины <tex> p[n] </tex> продолжаются одним символом, значит, надо на текущую позицию строки <tex> s </tex> поставить символ <tex> s[p[n]] </tex>. Если значение префикс-функции увеличивается, значит, текущим символом продолжается префикс длины <tex> p[n - 1] </tex>, а из свойств следует, что <tex> p[n - 1] \geqslant p[n] - 1 </tex>. По предположению индукцию значение <tex> q[n - 1] </tex> будет вычислено верно. А если значение префикс-функции не увеличивается, значит, символ <tex> s[n] </tex> должен продолжить префикс меньшей длины, а в текущее значение префикс-функции запишется как раз длина нового [[Период_и_бордер,_их_связь#Определения|бордера]]. Для этого будут использованы значения префикс-функции с меньшими индексами, которые посчитаны верно, опять же по предположению индукции. | ||
+ | |||
+ | == См. также == | ||
+ | *[[Z-функция|Z-функция]] | ||
+ | *[[Алгоритм Кнута-Морриса-Пратта|Алгоритм Кнута-Морриса-Пратта]] | ||
+ | |||
== Источники информации == | == Источники информации == | ||
*[http://e-maxx.ru/algo/prefix_function Префикс-функция — e-maxx] | *[http://e-maxx.ru/algo/prefix_function Префикс-функция — e-maxx] | ||
− | *[ | + | *[[wikipedia:ru:Префикс-функция | Википедия {{---}} Префикс-функция]] |
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. {{---}} 2-е изд. {{---}} М.: Издательский дом «Вильямс», 2007. {{---}} С. 1296 ISBN 978-5-8459-0857-5 | * Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. {{---}} 2-е изд. {{---}} М.: Издательский дом «Вильямс», 2007. {{---}} С. 1296 ISBN 978-5-8459-0857-5 | ||
− | |||
− | |||
− | |||
− | |||
Версия 21:24, 14 мая 2014
Определение: |
Префикс-функция (англ. prefix-function) от строки — массив длин наибольших бордеров для каждой позиции этой строки |
Здесь и далее считаем, что символы в строках нумеруются с
.Определим префикс-функцию от строки
в позиции следующим образом: . Если мы не нашли такого , то .Содержание
Наивный алгоритм
Наивный алгоритм вычисляет префикс функцию непосредственно по определению, сравнивая префиксы и суффиксы строк. Обозначим длину строки за
Псевдокод
int[] Prefix_function(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
Пример
Рассмотрим строку abcabcd, для которой значение префикс-функции равно
.Шаг | Строка | Значение функции |
---|---|---|
a | 0 | |
ab | 0 | |
abc | 0 | |
abca | 1 | |
abcab | 2 | |
abcabc | 3 | |
abcabcd | 0 |
Время работы
Всего
итераций цикла, на каждой из который происходит сравнение строк за , что дает в итоге .Эффективный алгоритм
Вносятся несколько важных замечаний:
- Заметим, что . Чтобы показать это, рассмотрим суффикс,оканчивающийся на позиции и имеющий длину , удалив из него последний символ, мы получим суффикс, оканчивающийся на позиции и имеющий длину , следовательно неравенство неверно.
- Избавимся от явных сравнений строк. Пусть мы вычислили бордеру наибольшей длины, для этого подберем такое , что . Делаем это следующим образом. За исходное необходимо взять , что следует из первого пункта. В случае, когда символы и не совпадают, — следующее потенциальное наибольшее значение , что видно из рисунка. Последнее утверждение верно, пока , что позволит всегда найти его следующее значение. Если , то при , иначе . , тогда, если , то . Если окажется, что , то нужно попытаться попробовать подстроку меньшей длины. Хотелось бы сразу перейти к такому
Псевдокод
int[] Prefix_function(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
Доказательство корректности алгоритма
Докажем, что если нам дали корректную префикс-функцию, то наш алгоритм построит строку с такой же префикс-функцией. Также заметим, что строк с такой префикс-функцией может быть много, и алгоритм строит только одну из них.
Пусть
данная префикс-функция, правильная строка, строку построил наш алгоритм, массив значений префикс-функции для .Докажем корректность индукцией по длине массива префикс-функции полученной строки. Для начала заметим, что на предыдущие значения массива
прибавление нового символа не влияет, так как при подсчёте префикс-функции на -ой позиции рассматриваются символы на позициях не больше . Поэтому достаточно показать, что очередное значение префикс-функции будет вычислено правильно.- База очевидна для строки длины .
- Переход: пусть до
- . Тогда мы добавляем новый символ, поэтому тоже будет равно .
- бордера. Для этого будут использованы значения префикс-функции с меньшими индексами, которые посчитаны верно, опять же по предположению индукции. . По свойствам префикс-функции — суффикс и префикс строки длины продолжаются одним символом, значит, надо на текущую позицию строки поставить символ . Если значение префикс-функции увеличивается, значит, текущим символом продолжается префикс длины , а из свойств следует, что . По предположению индукцию значение будет вычислено верно. А если значение префикс-функции не увеличивается, значит, символ должен продолжить префикс меньшей длины, а в текущее значение префикс-функции запишется как раз длина нового
-ой позиции мы построили строку, что . Возможны два случая:
См. также
Источники информации
- Префикс-функция — e-maxx
- Википедия — Префикс-функция
- Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296 ISBN 978-5-8459-0857-5