Префикс-функция — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Оптимизация)
(Время работы)
Строка 56: Строка 56:
  
 
===Время работы===
 
===Время работы===
В итоге мы получили алгоритм выполняющий <tex>O(n)</tex> итераций за <tex>O(1)</tex>, что дает нам итоговое <tex>O(n)</tex>.
+
С помощью метода потенциалов можно показать, что время работы <tex>O(n)</tex>. Потенциал величины <tex>k</tex> связывается с текущим ее значением в алгоритме. Начальное значение этого потенциала равно нулю. На каждой итерации цикла <tex>while</tex> значение <tex>k</tex> уменьшается, поскольку <tex>\pi(k) < k</tex>. Однако в силу <tex>\pi(k) \ge 0</tex> значение этой переменной не бывает отрицательным. Также значение <tex>k</tex> изменяется не более чем на 1 внутри тела цикла <tex>for</tex>. Поскольку перед входом в цикл выполняется <tex> k < i</tex> и поскольку значение переменной <tex>i</tex> увеличивается в каждой итерации цикла <tex>for</tex>, справедливость неравенства <tex>k < i</tex> сохраняется (подтверждая тот факт, что соблюдается также неравенство <tex>\pi(i) < i </tex>. Каждое выполнение тела цикла <tex>while</tex> можно оплатить соответствующим уменьшение потенциальной функции, поскольку <tex>\pi(k) < k </tex>. Кроме этого значение потенциальной функции возрастает не более чем на 1, из-за этого амортизированная стоимость тела цикла <tex>for</tex> {{---}} <tex>O(1)</tex>. Так как количество итераций <tex>O(n)</tex>, и поскольку конечное значение потенциальной функции по величине не меньше, чем ее начальное значение, полное время работы в наихудшем случае равно <tex>O(n)</tex>.
  
 
== Литература ==
 
== Литература ==
 
Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. {{---}} 2-е изд. {{---}} М.: Издательский дом «Вильямс», 2007. {{---}} С. 1296.
 
Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. {{---}} 2-е изд. {{---}} М.: Издательский дом «Вильямс», 2007. {{---}} С. 1296.

Версия 20:26, 8 июня 2012

Префикс-функция строки [math]s[/math] — функция [math]\pi(i) = max \{ k | k \lt i,[/math] [math]s[1..k] = s[i - k + 1..i] \}[/math].

Алгоритм

Наивный алгоритм вычисляет префикс функцию непосредственно по определению, сравнивая префиксы и суффиксы строк.

Пример

Рассмотрим строку abcabcd, для которой значение префикс-функции равно [math][0,0,0,1,2,3,0][/math].

Шаг Строка Значение функции
[math]1[/math] a 0
[math]2[/math] ab 0
[math]3[/math] abc 0
[math]4[/math] abca 1
[math]5[/math] abcab 2
[math]6[/math] abcabc 3
[math]7[/math] abcabcd 0

Псевдокод

Prefix_function ([math]s[/math])
     [math]\pi[/math] = []
     for i = 1 to n
         for k = 1 to i - 1
             if s[1..k] == s[i - k + 1..i]
                 [math]\pi[/math][i] = k
     return [math]\pi[/math]

Время работы

Всего [math]O(n^2)[/math] итераций цикла, на каждой из который происходит сравнение строк за [math]O(n)[/math], что дает в итоге [math]O(n^3)[/math].

Оптимизация

Внесем несколько важных замечаний:

  • [math]\pi(i)[/math] превосходит [math]\pi(i-1)[/math] не больше, чем на [math]1[/math]. Действительно, если [math]\pi(i) \gt \pi(i-1) + 1[/math], тогда [math]\pi(i) - 1 \gt \pi(i-1)[/math], значит в [math]\pi(i-1)[/math] не максимально возможное значение, получили противоречие.
  • Избавимся от явных сравнений строк. Пусть мы вычислили [math]\pi(i-1)[/math] и [math]s[\pi(i-1) + 1] = s[i][/math], тогда очевидно [math]\pi(i) = \pi(i-1) + 1[/math]. Если же условие [math]s[\pi(i) + 1] = s[i + 1][/math] ложно, то хотелось бы найти наибольшую длину [math] k[/math], для которой верно [math]\pi(i) = k + 1[/math]. Когда мы найдем такое [math]k[/math] нам достаточно будет сравнить [math]s[k + 1][/math] и [math]s[i][/math], при их равенстве [math]\pi(i) = k + 1[/math] будет верно. Будем искать наше [math]k[/math] пока оно больше нуля, при равенстве нулю [math]\pi(i) = 1[/math], если [math]s[i] = s[1][/math], иначе нулю. Общая схема алгоритма у нас есть, теперь нужно только научиться искать [math]k[/math].
  • Для поиска [math]k[/math] нам стоит использовать равенство [math]k = \pi(k)[/math], когда [math]s[k+1] = s[i][/math] ложно, взяв за исходное [math] k = \pi(i -1)[/math], это позволит выбирать [math]k[/math] по убыванию вплоть до нуля, так как очевидно, что [math]\pi(x) \geq \pi(\pi(x))[/math] для любых [math]x[/math].

Последние два пункта наглядно проиллюстрированы на рисунке:

Prefix2.jpg

Псевдокод

Prefix_function ([math]s[/math])
     [math]\pi[/math] = 0
     for i = 2 to n
         k = [math]\pi[/math][i - 1] 
         while k > 0 && s[i] != s[k + 1]
             k = [math]\pi[/math][k]
         if s[i] == s[k + 1]
             k++
         [math]\pi[/math][i] = k
     return [math]\pi[/math]

Время работы

С помощью метода потенциалов можно показать, что время работы [math]O(n)[/math]. Потенциал величины [math]k[/math] связывается с текущим ее значением в алгоритме. Начальное значение этого потенциала равно нулю. На каждой итерации цикла [math]while[/math] значение [math]k[/math] уменьшается, поскольку [math]\pi(k) \lt k[/math]. Однако в силу [math]\pi(k) \ge 0[/math] значение этой переменной не бывает отрицательным. Также значение [math]k[/math] изменяется не более чем на 1 внутри тела цикла [math]for[/math]. Поскольку перед входом в цикл выполняется [math] k \lt i[/math] и поскольку значение переменной [math]i[/math] увеличивается в каждой итерации цикла [math]for[/math], справедливость неравенства [math]k \lt i[/math] сохраняется (подтверждая тот факт, что соблюдается также неравенство [math]\pi(i) \lt i [/math]. Каждое выполнение тела цикла [math]while[/math] можно оплатить соответствующим уменьшение потенциальной функции, поскольку [math]\pi(k) \lt k [/math]. Кроме этого значение потенциальной функции возрастает не более чем на 1, из-за этого амортизированная стоимость тела цикла [math]for[/math][math]O(1)[/math]. Так как количество итераций [math]O(n)[/math], и поскольку конечное значение потенциальной функции по величине не меньше, чем ее начальное значение, полное время работы в наихудшем случае равно [math]O(n)[/math].

Литература

Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.