Алгоритм Манакера — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 24: Строка 24:
 
1. <tex>i > r</tex>, т.е. текущая позиция не попадает в границы самого правого из найденных палиндромов. Тогда просто запустим наивный алгоритм для позиции <tex>i</tex>.
 
1. <tex>i > r</tex>, т.е. текущая позиция не попадает в границы самого правого из найденных палиндромов. Тогда просто запустим наивный алгоритм для позиции <tex>i</tex>.
  
2. <tex>i \leq r</tex>. Тогда попробуем воспользоваться значениями, посчитанным ранее. Отразим нашу текущую позицию внутри палиндрома <tex>[l;r] : j = (r - i) + l</tex>. Поскольку <tex>i</tex> и <tex>j</tex> - симметричные позиции, то мы можем утверждать, <tex>d1[i] = d1[j]</tex>. Однако надо не забыть про один граничный случай: что если <tex>i + d1[j] - 1</tex> выходит за границы самого правого палиндрома? Так как информации о том, что происходит за границами это палинлрома у нас нет, то необходимо ограничить значение <tex>d1[i]</tex> следующим образом: <tex>d1[i] = min(r - i, d1[j]). После этого запустим наивный алгоритм, который будет увеличивать значение <tex>d1[i]</tex>, пока это возможно.
+
2. <tex>i \leq r</tex>. Тогда попробуем воспользоваться значениями, посчитанным ранее. Отразим нашу текущую позицию внутри палиндрома <tex>[l;r] : j = (r - i) + l</tex>. Поскольку <tex>i</tex> и <tex>j</tex> - симметричные позиции, то мы можем утверждать, <tex>d1[i] = d1[j]</tex>. Однако надо не забыть про один граничный случай: что если <tex>i + d1[j] - 1</tex> выходит за границы самого правого палиндрома? Так как информации о том, что происходит за границами это палинлрома у нас нет, то необходимо ограничить значение <tex>d1[i]</tex> следующим образом: <tex>d1[i] = min(r - i, d1[j])</tex>. После этого запустим наивный алгоритм, который будет увеличивать значение <tex>d1[i]</tex>, пока это возможно.
 +
 
 +
После каждого шага важно не забывать обновлять значения <tex>[l;r]</tex>
  
 
Заметим, что массив <tex>d2</tex> считается аналогичным образом, нужно лишь немного изменить индексы.
 
Заметим, что массив <tex>d2</tex> считается аналогичным образом, нужно лишь немного изменить индексы.
  
 
===Псевдокод===
 
===Псевдокод===
Приведем код, который вычисляет значения массивов <tex>d1</tex> и <tex>d2</tex>
+
Приведем код, который вычисляет значения массива <tex>d1</tex>:
 +
 
 +
<font color=green>// <tex>s</tex> {{---}} исходная строка</font>
 +
'''int''' l = 0
 +
'''int''' r = -1
 +
'''for''' i = 1 '''to''' n
 +
  '''int''' k = 0
 +
  '''if''' i <= r
 +
      k = min(r - i, d[r - i + l])
 +
  '''while''' i + k + 1 <= n '''and''' i - k - 1 > 0 '''and''' s[i + k + 1] == s[i - k - 1]
 +
      k++
 +
    d1[i] = k
 +
    '''if''' i + k > r
 +
      l = i - k
 +
      r = i + k
  
 +
Вычисление значений массива <tex>d2</tex>:
 
  <font color=green>// <tex>s</tex> {{---}} исходная строка</font>
 
  <font color=green>// <tex>s</tex> {{---}} исходная строка</font>
<font color=green>// <tex>d1, d2</tex> {{---}} массивы для записи ответа</font>
 
 
  '''int''' l = 0
 
  '''int''' l = 0
 
  '''int''' r = -1
 
  '''int''' r = -1
 
  '''for''' i = 1 '''to''' n
 
  '''for''' i = 1 '''to''' n
   '''int''' k = 1
+
   '''int''' k = 0
 
   '''if''' i <= r
 
   '''if''' i <= r
       k = k + min(r - i, d[r - i + l])
+
       k = min(r - i + 1, d[r - i + l + 1])
   '''while''' i + k <= n '''and''' i - k > 0 '''and''' s[i + k] == s[i - k]
+
   '''while''' i + k <= n '''and''' i - k - 1 > 0 '''and''' s[i + k] == s[i - k - 1]
 
       k++
 
       k++
    d1[i]++
+
    d2[i] = k
  d2[i] = 0
+
    '''if''' i + k - 1 > r
  '''while''' i - d2[i] - 1 > 0 '''and''' i + d2[i] <= n '''and''' s[i - d2[i] - 1] == s[i + d2[i]]
+
      l = i - k
    d2[i]++
+
      r = i + k - 1

Версия 22:39, 10 марта 2016

Задача:
Пусть дана строка [math]s[/math]. Требуется найти [math]d1[i][/math] - длина наибольшего палиндрома нечетной длины с центром в позиции [math]i[/math] и [math]d2[i][/math]- аналогично для палиндромов четной длины для всех [math]i[/math] от 1 до [math]|s|[/math].


Наивный алгоритм

Идея

Опишем сначала наивный алгоритм решения задачи. Чтобы посчитать ответ для позиции [math]i[/math], будем на каждом шаге увеличивать длину палиндрома с центром в [math]i[/math] и убеждаться, что рассматриваемая строка не перестала быть палиндромом, либо не произошел выход за границы массива. Очевидно, что такой алгоритм будет работать за [math]O(n^2)[/math]

Псевдокод

// [math]s[/math] — исходная строка
// [math]d1, d2[/math] — массивы для записи ответа
 for i = 1 to n
   d1[i] = 1
   while i - d1[i] > 0 and i + d1[i] <= n and s[i - d1[i]] == s[i + d1[i]]
     d1[i]++
   d2[i] = 0
   while i - d2[i] - 1 > 0 and i + d2[i] <= n and s[i - d2[i] - 1] == s[i + d2[i]]
     d2[i]++

Алгоритм Манакера

Идея

Алгоритм, который будет описан далее, отличается от наивного тем, что использует значения, посчитанные ранее. Будем поддерживать границы самого правого из найденных палиндромов - [math][l; r][/math]. Итак, пусть мы хотим вычислить [math]d1[i][/math] - т.е. длину наибольшего палиндрома с центром в позиции [math]i[/math]. При этом все предыдущие значения в массиве [math]d[/math] уже посчитаны. Возможны два случая:

1. [math]i \gt r[/math], т.е. текущая позиция не попадает в границы самого правого из найденных палиндромов. Тогда просто запустим наивный алгоритм для позиции [math]i[/math].

2. [math]i \leq r[/math]. Тогда попробуем воспользоваться значениями, посчитанным ранее. Отразим нашу текущую позицию внутри палиндрома [math][l;r] : j = (r - i) + l[/math]. Поскольку [math]i[/math] и [math]j[/math] - симметричные позиции, то мы можем утверждать, [math]d1[i] = d1[j][/math]. Однако надо не забыть про один граничный случай: что если [math]i + d1[j] - 1[/math] выходит за границы самого правого палиндрома? Так как информации о том, что происходит за границами это палинлрома у нас нет, то необходимо ограничить значение [math]d1[i][/math] следующим образом: [math]d1[i] = min(r - i, d1[j])[/math]. После этого запустим наивный алгоритм, который будет увеличивать значение [math]d1[i][/math], пока это возможно.

После каждого шага важно не забывать обновлять значения [math][l;r][/math]

Заметим, что массив [math]d2[/math] считается аналогичным образом, нужно лишь немного изменить индексы.

Псевдокод

Приведем код, который вычисляет значения массива [math]d1[/math]:

// [math]s[/math] — исходная строка
int l = 0
int r = -1
for i = 1 to n
  int k = 0
  if i <= r
     k = min(r - i, d[r - i + l])
  while i + k + 1 <= n and i - k - 1 > 0 and s[i + k + 1] == s[i - k - 1]
     k++
   d1[i] = k
   if i + k > r
     l = i - k
     r = i + k

Вычисление значений массива [math]d2[/math]:

// [math]s[/math] — исходная строка
int l = 0
int r = -1
for i = 1 to n
  int k = 0
  if i <= r
     k = min(r - i + 1, d[r - i + l + 1])
  while i + k <= n and i - k - 1 > 0 and s[i + k] == s[i - k - 1]
     k++
   d2[i] = k
   if i + k - 1 > r
     l = i - k
     r = i + k - 1