Изменения
→Описание алгоритма
Z-блоком назовем подстроку с началом в позиции <tex>i</tex> и длиной <tex>Z[i]</tex>.<br>
Для работы алгоритма заведём две переменные: <tex>left</tex> и <tex>right</tex> — начало и конец Z-блока строки <tex>S</tex> с максимальной позицией конца <tex>right</tex> (среди всех таких Z-блоков, если их несколько, выбирается наибольший). Изначально <tex>left=0</tex> и <tex>right=0</tex>.
Пусть нам известны значения Z-функции от <tex>0</tex> до <tex>i-1</tex>. Найдём <tex>Z[i]</tex>.
Рассмотрим два случая.
# <tex>i > right</tex>:<br><!--
-->Просто пробегаемся по строке <tex>S</tex> и сравниваем символы на позициях <tex>S[i+j]</tex> и <tex>S[j]</tex>.<!--
-->Пусть <tex>j</tex> первая позиция в строке <tex>S</tex> для которой не выполняется равенство <tex>S[i+j] = S[j]</tex>, тогда <tex>j</tex> это и Z-функция для позиции <tex>i</tex>. Тогда <tex>left = i, right = i + j - 1</tex>. В данном случае будет определено корректное значение <tex>Z[i]</tex> в силу того, что оно определяется наивно, путем сравнения с начальными символами строки.
# <tex>i \leqslant right</tex>:<br><!--
-->Сравним <tex>Z[i - left] + i</tex> и <tex>right</tex>. Если <tex>right</tex> меньше, то надо просто наивно пробежаться по строке начиная с позиции <tex>right</tex> и вычислить значение <tex>Z[i]</tex>. Корректность в таком случае также гарантирована.<!--
-->Иначе мы уже знаем верное значение <tex>Z[i]</tex>, так как оно равно значению <tex>Z[i - left]</tex>.
[[Файл:z-func.png]]
=== Время работы ===
Этот алгоритм работает за <tex>O(|S|)</tex>, так как каждая позиция пробегается не более двух раз: при попадании в диапазон от <tex>left</tex> до <tex>right</tex> и при высчитывании Z-функции простым циклом.
=== Псевдокод ===
'''int'''[] zFunction(s : '''string'''):
'''int'''[] zf = '''int'''[n]
'''int''' left = 0, right = 0
'''for''' i = 1 '''to''' n − 1
zf[i] = max(0, min(right − i, zf[i − left]))
'''while''' i + zf[i] < n '''and''' s[zf[i]] == s[i + zf[i]]
zf[i]++
'''if''' i + zf[i] > right
left = i
right = i + zf[i]
'''return''' zf
== Поиск подстроки в строке с помощью Z-функции ==
<tex>n</tex> — длина текста. <tex>m</tex> — длина образца. <br>
Образуем строку <tt>s = pattern + # + text</tt>, где <tt>#</tt> — символ, не встречающийся ни в <tt>text</tt>, ни в <tt>pattern</tt>. Вычисляем Z-функцию от этой строки.
В полученном массиве, в позициях в которых значение Z-функции равно <tex>|\texttt{pattern}|</tex>, по определению начинается подстрока, совпадающая с <tt>pattern</tt>.
=== Псевдокод ===
'''int''' substringSearch(text : '''string''', pattern : '''string'''):
'''int'''[] zf = zFunction(pattern + '#' + text)
'''for''' i = m + 1 '''to''' n + 1
'''if''' zf[i] == m
'''return''' i
==Построение строки по Z-функции==
{{Задача
|definition= Необходимо восстановить строку по Z-функции, считая алфавит ограниченным.
}}
===Описание алгоритма===
Пусть в массиве <tex>z</tex> хранятся значения Z-функции, в <tex>s</tex> будет записан ответ. Пойдем по массиву <tex>z</tex> слева направо.
Нужно узнать значение <tex>s[i]</tex>. Для этого посмотрим на значение <tex>z[i]</tex>: если <tex>z[i] = 0</tex>, тогда в <tex>s[i]</tex> запишем ещё не использованный символ или последний использованный символ алфавита, если мы уже использовали все символы. Если <tex>z[i] \neq 0</tex>, то нам нужно записать префикс длины <tex>z[i]</tex> строки <tex>s</tex>. Но если при посимвольном записывании этого префикса в конец строки <tex>s</tex> мы нашли такой <tex>j</tex> (индекс последнего символа строки), что <tex>z[j]</tex> больше, чем длина оставшейся незаписанной части префикса, то мы перестаём писать этот префикс и пишем префикс длиной <tex>z[j]</tex> строки <tex>s</tex>.
Для правильной работы алгоритма будем считать значение <tex>z[0]</tex> равным нулю.
Заметим, что не всегда удастся восстановить строку с ограниченным алфавитом неподходящего размера. Например, для строки <tex>abacaba</tex> массив Z-функций будет <tex>[0, 0, 1, 0, 3, 0, 1]</tex>. Используя двоичный алфавит, мы получим строку <tex>abababa</tex>, но её массив Z-функций отличается от исходного. Ошибка восстановления строки возникла, когда закончились новые символы алфавита.
Если строить строку по некорректному массиву значений Z-функции, то мы получим какую-то строку, но массив значений Z-функций от неё будет отличаться от исходного.
=== Время работы ===
Этот алгоритм работает за O(|S|), так как мы один раз проходим по массиву Z-функций.
=== Реализация ===
'''string''' buildFromZ(z : '''int'''[], alphabet : '''char'''[]):
'''string''' s = ""
'''int''' prefixLength = 0 <font color=green>// длина префикса, который мы записываем</font>
'''int''' j <font color=green>// позиция символа в строке, который будем записывать</font>
'''int''' newCharacter = 0 <font color=green>// индекс нового символа</font>
'''for''' i = 0 '''to''' z.length - 1
<font color=green>// мы не пишем какой-то префикс и не будем писать новый</font>
'''if''' z[i] = 0 '''and''' prefixLength = 0
if newCharacter < alphabet.length
s += alphabet[newCharacter]
newCharacter++
else
s += alphabet[newCharacter - 1]
<font color=green>// нам нужно запомнить, что мы пишем префикс </font>
'''if''' z[i] > prefixLength
prefixLength = z[i]
j = 0
<font color=green>// пишем префикс</font>
'''if''' prefixLength > 0
s += s[j]
j++
prefixLength--
'''return''' s
===Доказательство корректности алгоритма===
Докажем, что если нам дали корректную Z-функцию, то наш алгоритм построит строку с такой же Z-функцией.
Пусть <tex>z</tex> — данная Z-функция, строку <tex>s</tex> построил наш алгоритм, <tex>q</tex> — массив значений Z-функции для <tex>s</tex>. Покажем, что массивы <tex>q</tex> и <tex>z</tex> будут совпадать.
[[Файл: Запись_префикса.png|330px|thumb|right|Записали префикс, начинающийся в <tex>i</tex>. После пишем префикс, начинающийся в <tex>j</tex>. Этот префикс не изменит символы первого префикса.]]
Рассмотрим похожий алгоритм, но с более худшей асимптотикой. Отличие будет в том, что при <tex>z[i] > 0</tex> мы будем писать префикс полностью и возвращаться в позицию <tex>i + 1</tex>. Рассмотрим каждый шаг этого алгоритма. Если <tex>z[i] = 0</tex>, то мы пишем символ, отличный от первого символа строки, поэтому <tex>q[i] = 0</tex>, а значит <tex>q[i] = z[i]</tex>. Если <tex>z[i] > 0</tex>, то при записи <tex>s[i]</tex> мы будем получать <tex>q[i] = z[i]</tex>, потому что мы переписали префикс строки. Но далее мы можем переписать этот префикс другим префиксом. Заметим, что новый префикс будет содержаться и в префиксе самой строки, поэтому пересечение двух префиксов будет состоять из одинаковых символов. Значит, префикс не будет изменяться, как и значение <tex>q[i]</tex>. Тогда массив <tex>q</tex> совпадает с <tex>z</tex>.
Покажем, что этот алгоритм эквивалентен нашему алгоритму. Когда мы пишем разные префиксы, то возможны три варианта: они не пересекаются (начало и конец одного префикса не принадлежат другому), один лежит внутри другого (начало и конец префикса принадлежит другому), они пересекаются (начало одного префикса пренадлежит другому, но конец не принадлежит).
* Если префиксы не пересекаются, то в алгоритме они не влияют друг на друга.
[[Файл: Префиксы1.png|400px]]
* Если префикс лежит внутри другого префикса, то записав большой префикс мы запишем и малый, поэтому не нужно возвращаться к началу малого префикса.
[[Файл: Префиксы2.png|400px]]
* Если префиксы пересекаются, то нам нужно переписать часть префикса, который начинается раньше, и начать писать другой префикс (начало этого префикса запишет конец префикса, начинающегося раньше). Если полностью переписать префикс, начинающийся раньше, то мы не сможем восстановить префикс, который начинался раньше конца первого префикса.
[[Файл: Префиксы3.png|400px]]
Таким образом, алгоритмы эквивалентны и наш алгоритм тоже корректен.
==Построение Z-функции по префикс-функции==
{{Задача
|definition= Дан массив с корректной [[Префикс-функция | префикс-функцией]] для строки <tex>s</tex>. Требуется получить массив с Z-функцией для строки <tex>s</tex>.
}}[[Файл:Case one.png|300px|thumb|right|'''Случай первый''']]
[[Файл:Case two.png|300px|thumb|right|'''Случай второй''']]
[[Файл:Case three.png|300px|thumb|right|'''Случай третий''']]
<br>
===Описание алгоритма===
<br>
Пусть префикс функция хранится в массиве <tex>P[0 \ldots n - 1]</tex>. Z-функцию будем записывать в массив <tex>Z[0 \ldots n-1]</tex>. Заметим, что если <tex>P[i]>0</tex>, то мы можем заявить, что <tex>Z[i-P[i]+1]</tex> будет не меньше, чем <tex>P[i]</tex>.
<br>
<br>
Так же заметим, что после такого прохода в <tex>Z[1]</tex> будет максимальное возможное значение. Далее будем поддерживать инвариант: в <tex>Z[i]</tex> будет максимальное возможное значение.
<br>
===Время работы алгоритма=Источники информации ==Этот алгоритм работает за * [http://habrahabr.ru/post/113266/ Поиск подстроки и смежные вопросы — Хабр]<texbr>O(\lvert S\rvert)* [[wikipedia:ru:Z-функция | Википедия — Z-функция]]</texbr>, так как каждая позиция пробегается не более двух раз* [http: при попадании в диапазон от <tex>left</tex> до <tex>right</tex> codeforces.ru/blog/entry/9612/ Codeforces — Переход между Z- и при высчитывании Zпрефикс-функции простым циклом.функциями]