Префикс-функция — различия между версиями
Sergej (обсуждение | вклад) (→Доказательство корректности алгоритма) |
Sergej (обсуждение | вклад) (→Доказательство корректности алгоритма) |
||
Строка 88: | Строка 88: | ||
<tex>1)</tex> <tex>p[n] = 0</tex>. Тогда мы добавляем новый символ, поэтому <tex>q[n]</tex> тоже будет равно <tex>0</tex>. Также, предыдущие значения <tex>q</tex> не поменяются и останутся верными. | <tex>1)</tex> <tex>p[n] = 0</tex>. Тогда мы добавляем новый символ, поэтому <tex>q[n]</tex> тоже будет равно <tex>0</tex>. Также, предыдущие значения <tex>q</tex> не поменяются и останутся верными. | ||
− | <tex>2)</tex> <tex>p[n] > 0</tex>. Предположим, что <tex>q[n] \neq p[n] </tex>. Заметим, что подстрока с <tex>1</tex> по <tex>p[n]</tex> оканчивается на <tex>n</tex>-ом символе. По предположению индукции наш алгоритм построил правильную строку до <tex>n - 1</tex> символа, следовательно, <tex>p[n] \leqslant q[n]</tex>. | + | <tex>2)</tex> <tex>p[n] > 0</tex>. Предположим, что <tex>q[n] \neq p[n] </tex>. Заметим, что подстрока с <tex>1</tex> по <tex>p[n]</tex> оканчивается на <tex>n</tex>-ом символе. По предположению индукции наш алгоритм построил правильную строку до <tex>n - 1</tex> символа, следовательно, <tex>p[n] \leqslant q[n]</tex>. Представим, что <tex>q[n] > p[n] </tex>,тогда получается, что префикс с <tex>1..q[n]</tex> в строке <tex>s'</tex>, является подстрокой заканчивающийся на <tex>n</tex>-ом символе, но тогда возникает противоречие с тем, что массив <tex>p</tex> корректный |
Простой пример некорректного <tex>p = {0,1,1} </tex>, тогда по алгоритму получится строка <tex>aaa</tex>. Очевидно, что префикс-функции не будут совпадать. | Простой пример некорректного <tex>p = {0,1,1} </tex>, тогда по алгоритму получится строка <tex>aaa</tex>. Очевидно, что префикс-функции не будут совпадать. |
Версия 13:59, 2 мая 2014
Префикс-функция строки
— функция .Содержание
Алгоритм
Наивный алгоритм вычисляет префикс функцию непосредственно по определению, сравнивая префиксы и суффиксы строк.
Псевдокод
Prefix_function () = [0,..,0] for i = 1 to n for k = 1 to i - 1 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 k = 0 for i = 2 to n while k > 0 && s[i] != s[k + 1] k = [k] if s[i] == s[k + 1] k++ [i] = k return
Время работы
Время работы алгоритма составит
. Для доказательства этого нужно заметить, что итоговое количество итераций цикла определяет асимптотику алгоритма. Теперь стоит отметить, что увеличивается на каждом шаге не более чем на единицу, значит максимально возможное значение . Поскольку внутри цикла значение лишь уменьшается, получается, что не может суммарно уменьшиться больше, чем раз. Значит цикл в итоге выполнится не более раз, что дает итоговую оценку времени алгоритма .Построение строки по префикс-функции
Постановка задачи
Восстановить строку по префикс-функции за
(алфавит неограничен).Описание алгоритма
В дальнейшем нумерация символов в строках будет с
.Пусть в массиве
хранятся значения префикс-функции, в будет записан ответ. Пойдем по массиву слева направо. Пусть мы хотим узнать значение , посмотрим на значение , если тогда в запишем новый символ, иначе . Обратим внимание, что мы посчитали раньше, так как . Так как подстрока с по заканчивается в , то должен быть равен .Псевдокод
string buildFromPrefix(int[] p) for i = 0 to p.length - 1 if p[i] == 0 s += new char else s += s[p[i]] return s
Доказательство корректности алгоритма
Докажем, что если нам дали корректную префикс-функцию, то наш алгоритм построит строку с такой же префикс-функцией. Также заметим, что строк с такой префикс-функцией может быть много, и алгоритм строит только одну из них.
Пусть
данная префикс-функция, правильная строка, эту строку построил наш алгоритм, массив значений префикс-функции для .Докажем корректность индукцией по длине массива префикс-функции полученной строки.
База очевидна для строки длиной
.Переход: Пусть до
-ой позиции мы построили строку, что . Возможно два варианта,. Тогда мы добавляем новый символ, поэтому тоже будет равно . Также, предыдущие значения не поменяются и останутся верными.
. Предположим, что . Заметим, что подстрока с по оканчивается на -ом символе. По предположению индукции наш алгоритм построил правильную строку до символа, следовательно, . Представим, что ,тогда получается, что префикс с в строке , является подстрокой заканчивающийся на -ом символе, но тогда возникает противоречие с тем, что массив корректный
Простой пример некорректного
, тогда по алгоритму получится строка . Очевидно, что префикс-функции не будут совпадать.Литература
Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.