Задача о наименьшей суперпоследовательности — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 38: Строка 38:
  
 
===Псевдокод===
 
===Псевдокод===
x, y — данные последовательности; scs[i][j] — SCS для префикса длины i последовательности x и префикса длины j последовательности y; prev[i][j] — пара индексов элемента таблицы, соответствующего оптимальному решению вспомогательной задачи, выбранной при вычислении scs[i][j].
+
x, y — данные последовательности; <tex>scs[i][j] </tex> — SCS для префикса длины i последовательности x и префикса длины j последовательности y; prev[i][j] — пара индексов элемента таблицы, которые предшествовали <tex>scs[i][j]</tex>.
 +
 
 +
'''fun''' SCS(x: '''int''', y: '''int'''):    ''<font color="green">// аналог void </font>''
 +
    m = x.size
 +
    n = y.size
 +
    '''for''' i = 1 '''to''' m
 +
      scs[i][0] = i
 +
    '''for''' j = 0 '''to''' n
 +
      scs[0][j] = j
 +
    '''for''' i = 1 '''to''' m
 +
      '''for''' j = 1 '''to''' n
 +
        '''if''' x[i] == y[j]
 +
          scs[i][j] = 1 + scs[i - 1][j - 1]
 +
          prev[i][j] = pair(i - 1, j - 1)
 +
        '''else'''
 +
          '''if''' scs[i - 1][j] <= lcs[i][j - 1]
 +
            scs[i][j] = 1 + scs[i - 1][j]
 +
            prev[i][j] = pair(i - 1, j)
 +
          '''else'''
 +
            scs[i][j] = 1 + scs[i][j - 1]
 +
            prev[i][j] = pair(i, j - 1)
 +
 
 +
  '''int''' printLCS(m: '''int''', n: '''int'''):    ''<font color="green">// вывод SCS, вызывается как printLCS(m, n)</font>''
 +
    '''if''' i == 0 '''or''' j == 0  ''<font color="green">// пришли к началу LCS</font>''
 +
      '''return'''
 +
    '''if''' prev[i][j] == pair(i - 1, j - 1)  ''<font color="green">// если пришли в lcs[i][j] из lcs[i - 1][j - 1], то x[i] == y[j], надо вывести этот элемент</font>''
 +
      printLCS(i - 1, j - 1)
 +
      print x[i]
 +
    '''else'''
 +
      '''if''' prev[i][j] == pair(i - 1, j)
 +
        printLCS(i - 1, j)
 +
      '''else'''
 +
        printLCS(i, j - 1)

Версия 19:23, 23 декабря 2017

Определение:
Последовательность [math] Z = \left \langle z_1, z_2, \dots, z_k \right \rangle [/math] является суперпоследовательностью (англ. supersequence) последовательности [math] X = \left \langle x_1, x_2, \dots, x_m \right \rangle [/math], если существует строго возрастающая последовательность [math] \left \langle i_1, i_2, \dots, i_m \right \rangle [/math] индексов [math] Z [/math] таких, что для всех [math] j = 1, 2, \dots, m [/math] выполняется соотношение [math] z_{i_j} = x_j [/math].


Определение:
Последовательность [math] Z [/math] является общей суперпоследовательностью (англ. common supersequence) последовательностей [math] X [/math] и [math] Y [/math], если [math] X [/math] и [math] Y [/math] являются подпоследовательностями [math] Z [/math].


Задача:
Пусть имеются последовательности [math] X = \left \langle x_1, x_2, \dots, x_m \right \rangle [/math] и [math] Y = \left \langle y_1, y_2, \dots, y_n \right \rangle [/math]. Необходимо найти [math]SCS(X,Y)[/math]


Наивное решение

Заметим, что если приписать к одной из данной последовательность другую, то полученная последовательность будет их суперпоследовательностью с длиной [math] n + m [/math]. Запомним все элементы обеих последовательностей и из них построим все возможные последовательности. Тогда искомая [math]SCS[/math] гарантированно найдётся, однако время работы алгоритма будет экспоненциально зависеть от длины исходных последовательностей.


Динамическое программирование

Решение

Обозначим за [math] scs[i][j] [/math] SCS префиксов данных последовательностей [math] x[1 \dots n] [/math] и [math] y[1 \dots m] [/math], заканчивающихся в элементах с номерами [math] i [/math] и [math] j [/math] соответственно. Наименьшая общая суперпоследовательность [math] x[1 \dots i] [/math] и [math] y[1 \dots j] [/math] должна содержать каждый символ обеих последовательностей, поэтому если [math] j = 0 [/math], то [math] SCS [/math] это просто последовательность [math] x[1 \dots i] [/math]. Аналогичен случай, когда [math] i = 0 [/math]. Если [math] i \gt 0 [/math] и [math] j \gt 0 [/math], то возможны два случая. Если [math] x[i] \neq y[j] [/math], то SCS должна включать оба этих элемента. Значит нужно выбрать оптимальный из ответов для префиксов, включающих один элемент и не включающих второй, и выбрать из них минимальный. Если же [math] x[i] = y[j] [/math], то SCS для последовательностей [math] x[1 \dots i] [/math] и [math] y[1 \dots j] [/math] должна заканчиваться этим элементом, так как он общий для них. Получается следующее рекуррентное соотношение:

[math] scs[i][j] = \begin{cases} i, & j = 0 \\ j, & i = 0 \\ 1 + scs[i - 1][j - 1], & x[i] = y[j] \\ 1 + min(scs[i][j - 1],\ scs[i - 1][j]), & x[i] \neq y[j] \end{cases} [/math]

Очевидно, что сложность алгоритма составит [math] O(mn) [/math], где [math] m [/math] и [math] n [/math] — длины последовательностей.

Восстановление ответа

В [math] scs[i][j] [/math] помимо длины последовательности хранятся и символ, добавленный последним. Таким образом, зная длину SCS, можно восстановить и саму последовательность.

Псевдокод

x, y — данные последовательности; [math]scs[i][j] [/math] — SCS для префикса длины i последовательности x и префикса длины j последовательности y; prev[i][j] — пара индексов элемента таблицы, которые предшествовали [math]scs[i][j][/math].

fun SCS(x: int, y: int):    // аналог void 
   m = x.size
   n = y.size
   for i = 1 to m
     scs[i][0] = i
   for j = 0 to n
     scs[0][j] = j
   for i = 1 to m
     for j = 1 to n
       if x[i] == y[j]
         scs[i][j] = 1 + scs[i - 1][j - 1]
         prev[i][j] = pair(i - 1, j - 1)
       else
         if scs[i - 1][j] <= lcs[i][j - 1]
           scs[i][j] = 1 + scs[i - 1][j]
           prev[i][j] = pair(i - 1, j)
         else
           scs[i][j] = 1 + scs[i][j - 1]
           prev[i][j] = pair(i, j - 1)
 
 int printLCS(m: int, n: int):    // вывод SCS, вызывается как printLCS(m, n)
   if i == 0 or j == 0  // пришли к началу LCS
     return
   if prev[i][j] == pair(i - 1, j - 1)  // если пришли в lcs[i][j] из lcs[i - 1][j - 1], то x[i] == y[j], надо вывести этот элемент
     printLCS(i - 1, j - 1)
     print x[i]
   else
     if prev[i][j] == pair(i - 1, j)
       printLCS(i - 1, j)
     else
       printLCS(i, j - 1)