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

Материал из Викиконспекты
Перейти к: навигация, поиск
(Псевдокод)
Строка 7: Строка 7:
 
'''''Например''''', '''''HELOLEH''''' является подпоследовательностью-палиндромом строки '''''HTEOLFEOLEH'''''.  
 
'''''Например''''', '''''HELOLEH''''' является подпоследовательностью-палиндромом строки '''''HTEOLFEOLEH'''''.  
 
== Решение ==
 
== Решение ==
Обозначим данную последовательность через <tex>S</tex>, а ее элементы — через <tex>S[i], 1 \le i \le n</tex> Будем рассматривать возможные подпоследовательности данной последовательности с <tex>i - </tex>го по <tex>j-</tex>ый символ, обозначим их как <tex>S(i, j)</tex>. Длины максимальных палиндромов для подпоследовательностей будем записывать в квадратный массив <tex>L</tex>: <tex>L[i][j]</tex> — длина максимальной подпоследовательности-палиндрома, который можно получить из подпоследовательности <tex>S(i, j)</tex>.
+
Обозначим данную последовательность через <tex>S</tex>, а ее элементы — через <tex>S[i], 0 \le i \le n - 1</tex>. Будем рассматривать возможные подпоследовательности данной последовательности с <tex>i - </tex>го по <tex>j-</tex>ый символ включительно, обозначив её как <tex>S(i, j)</tex>. Длины максимальных подпалиндромов для данной последовательности будем записывать в квадратный массив <tex>L</tex>: <tex>L[i][j]</tex> — длина максимальной подпоследовательности-палиндрома, который можно получить из последовательности <tex>S(i, j)</tex>.
  
 
Начнем решать задачу с простых подпоследовательностей. Для последовательности из одного элемента (то есть подпоследовательности вида <tex>S(i, i)</tex>) ответ очевиден — ничего вычеркивать не надо, такая строка будет искомой подпоследовательностью-палиндромом. Для последовательности из двух элементов <tex>S(i, i + 1)</tex> возможны два варианта: если элементы равны, то мы имеем подпоследовательность-палиндром, ничего вычеркивать не надо. Если же элементы не равны, то вычеркиваем любой.
 
Начнем решать задачу с простых подпоследовательностей. Для последовательности из одного элемента (то есть подпоследовательности вида <tex>S(i, i)</tex>) ответ очевиден — ничего вычеркивать не надо, такая строка будет искомой подпоследовательностью-палиндромом. Для последовательности из двух элементов <tex>S(i, i + 1)</tex> возможны два варианта: если элементы равны, то мы имеем подпоследовательность-палиндром, ничего вычеркивать не надо. Если же элементы не равны, то вычеркиваем любой.
  
Пусть теперь нам дана подпоследовательность <tex>S(i, j)</tex>. Если первый <tex>(S[i])</tex> и последний <tex>(S[j])</tex> элементы подпоследовательности не совпадают, то один из них нужно вычеркнуть. Тогда у нас останется подпоследовательность <tex>S(i, j - 1)</tex> или <tex>S(i + 1, j)</tex> — то есть мы сведем задачу к подзадаче: <tex>L[i][j] = max(L[i][j - 1], L[i + 1][j])</tex>. Если же первый и последний элементы равны, то мы можем оставить оба, но необходимо знать решение задачи <tex>S(i + 1, j - 1):  
+
Пусть теперь нам дана подпоследовательность <tex>S(i, j)</tex>. Если <tex>S[i]</tex> и <tex>S[j]</tex> элементы подпоследовательности не совпадают, то один из них нужно вычеркнуть. Тогда у нас останется подпоследовательность <tex>S(i, j - 1)</tex> или <tex>S(i + 1, j)</tex> — то есть мы сведем задачу к подзадаче: <tex>L[i][j] = max(L[i][j - 1], L[i + 1][j])</tex>. Если же первый и последний элементы равны, то мы можем оставить оба, но необходимо знать решение задачи <tex>S(i + 1, j - 1):  
 
L[i][j] = L[i + 1][j - 1] + 2</tex>.
 
L[i][j] = L[i + 1][j - 1] + 2</tex>.
 
=== Асимптотика ===
 
=== Асимптотика ===
Строка 17: Строка 17:
  
 
== Пример ==
 
== Пример ==
Рассмотрим решение на примере последовательности '''''ABACCBA'''''. Первым делом заполняем диагональ массива единицами, они будут соответствовать подпоследовательностями <tex>S(i, i)</tex> из одного элемента. Затем начинаем рассматривать подпоследовательности длины два. Во всех подпоследовательностях, кроме <tex>S(4, 5)</tex>, элементы различны, поэтому в соответствующие ячейки запишем <tex>1</tex>, а в <tex>L[3][4]</tex> — <tex>2</tex>.
+
Рассмотрим решение на примере последовательности '''''ABACCBA'''''. Первым делом заполняем диагональ массива единицами, они будут соответствовать подпоследовательностями <tex>S(i, i)</tex> из одного элемента. Затем начинаем рассматривать подпоследовательности длины два. Во всех подпоследовательностях, кроме <tex>S(3, 4)</tex>, элементы различны, поэтому в соответствующие ячейки запишем <tex>1</tex>, а в <tex>L[3][4]</tex> — <tex>2</tex>.
  
 
Получается, что мы будем заполнять массив по диагоналям, начиная с главной диагонали. Для подпоследовательностей длины <tex>3</tex> получаются следующие значения: в подпоследовательности '''''ABA''''' первый и последний элемент равны, поэтому <tex>L[0][2] = L[1][1] + 2</tex>. В остальных подпоследовательностях первый и последний элементы различны.
 
Получается, что мы будем заполнять массив по диагоналям, начиная с главной диагонали. Для подпоследовательностей длины <tex>3</tex> получаются следующие значения: в подпоследовательности '''''ABA''''' первый и последний элемент равны, поэтому <tex>L[0][2] = L[1][1] + 2</tex>. В остальных подпоследовательностях первый и последний элементы различны.
Строка 29: Строка 29:
 
'''''CBA''''': <tex>L[4][6] = max(L[4][5], L[5][6]) = 1</tex>
 
'''''CBA''''': <tex>L[4][6] = max(L[4][5], L[5][6]) = 1</tex>
  
Продолжая далее аналогичные рассуждения, заполним все ячейки над диагональю и в ячейке <tex>L[0][6]</tex> получим ответ <tex>(6)</tex>.
+
Продолжая далее аналогичные рассуждения, заполним все ячейки над диагональю и в ячейке <tex>L[0][6]</tex> получим ответ {{---}} <tex>6</tex>.
  
 
Если же в задаче необходимо вывести не длину, а саму подпоследовательность-палиндром, то дополнительно к массиву длин мы должны построить массив переходов — для каждой ячейки запомнить, какой из случаев был реализован.
 
Если же в задаче необходимо вывести не длину, а саму подпоследовательность-палиндром, то дополнительно к массиву длин мы должны построить массив переходов — для каждой ячейки запомнить, какой из случаев был реализован.
Строка 43: Строка 43:
 
== Псевдокод ==
 
== Псевдокод ==
 
Перед вызовом процедуры заполняем <tex>L[][]</tex> начальными значениями: <tex>L[i][j] = 1</tex> если <tex>i=j</tex>, <tex>L[i][j] = 0</tex>, если <tex>i>j</tex>, в остальных случаях <tex>L[i][j]=-1</tex>.  
 
Перед вызовом процедуры заполняем <tex>L[][]</tex> начальными значениями: <tex>L[i][j] = 1</tex> если <tex>i=j</tex>, <tex>L[i][j] = 0</tex>, если <tex>i>j</tex>, в остальных случаях <tex>L[i][j]=-1</tex>.  
При первой вызове функции, к качестве аргументов передаем индексы первого и последнего элементов исходной строки. Например для строки длиной 7 вызов функции будет иметь следующий вид: <tex>palSubSeq(0,6)</tex>. Искомая же длина будет записана в ячейке <tex>L[0][N-1]</tex>, где <tex>N</tex> — длина исходной строки.
+
При первом вызове функции в качестве аргументов передаем индексы первого и последнего элементов исходной строки. Например для строки длиной <tex> N </tex> вызов функции будет иметь следующий вид: <tex>palSubSeq(0,N - 1)</tex>. Искомая же длина будет записана в ячейке <tex>L[0][N-1]</tex>.
  
'''Функция для вычисления длины палиндрома (<tex>left, right</tex> - границы исходной последовательности):'''
+
'''Функция для вычисления длины палиндрома''' (<tex>left, right</tex> - границы исходной последовательности):
 
<code style = "display: inline-block;">
 
<code style = "display: inline-block;">
 
   '''palSubSeq'''(left, right)
 
   '''palSubSeq'''(left, right)
Строка 55: Строка 55:
 
     '''return''' L[left][right]
 
     '''return''' L[left][right]
 
</code>
 
</code>
'''Функция для построения искомого палиндрома (<tex>left, right</tex> - границы исходной последовательности, <tex>l1=1, l2=L[1][n]</tex>):'''
+
'''Функция для построения искомого палиндрома''' (<tex>left, right</tex> {{---}} границы исходной последовательности, <tex>palLeft=0, palRight=L[0][n-1]</tex> {{---}} границы искомой):
 
<code style = "display: inline-block;">
 
<code style = "display: inline-block;">
   // pal {{---}} массив Char, где в pal[i] содержится символ искомой последовательности-палендрома
+
   // palindrome {{---}} массив символов, где в palindrome[i] содержится символ искомой последовательности-палиндрома
 
   '''palChars'''(left, right, l1, l2)
 
   '''palChars'''(left, right, l1, l2)
 
     '''while''' left <tex>\le</tex> right
 
     '''while''' left <tex>\le</tex> right
 
       '''if''' left == right && L[left][right] == 1
 
       '''if''' left == right && L[left][right] == 1
         Pal[l1++] = S[left]
+
         palindrome[palLeft++] = S[left++]
        left++
 
 
       '''else'''
 
       '''else'''
         '''if''' s[left] == s[right]
+
         '''if''' S[left] == S[right]
           Pal[l1++] = s[left]
+
           palindrome[palLeft++] = S[left++]
           Pal[l2--] = s[right]
+
           palindrome[palRight--] = S[right--]
          left++
 
          right--
 
 
         '''else'''
 
         '''else'''
           '''if''' L[left + 1][right] > L[left][right - 1]
+
           '''if''' L[left + 1][right] <tex> > </tex> L[left][right - 1]
 
             left++
 
             left++
 
           '''else'''
 
           '''else'''

Версия 12:42, 20 декабря 2012

Задача о наибольшей подпоследовательности-палиндрома — это задача поиска длины наибольшей подпоследовательности-палиндрома, которую можно получить вычеркиванием некоторых букв из данной последовательности.


Определения

Определение:
Палиндромом называется строка, которая одинаково читается как слева направо, так и справа налево.


Определение:
Подпоследовательностью-палиндромом данной строки называется последовательность символов из данной строки, не обязательно идущих подряд, являющаяся палиндромом.

Например, HELOLEH является подпоследовательностью-палиндромом строки HTEOLFEOLEH.

Решение

Обозначим данную последовательность через [math]S[/math], а ее элементы — через [math]S[i], 0 \le i \le n - 1[/math]. Будем рассматривать возможные подпоследовательности данной последовательности с [math]i - [/math]го по [math]j-[/math]ый символ включительно, обозначив её как [math]S(i, j)[/math]. Длины максимальных подпалиндромов для данной последовательности будем записывать в квадратный массив [math]L[/math]: [math]L[i][j][/math] — длина максимальной подпоследовательности-палиндрома, который можно получить из последовательности [math]S(i, j)[/math].

Начнем решать задачу с простых подпоследовательностей. Для последовательности из одного элемента (то есть подпоследовательности вида [math]S(i, i)[/math]) ответ очевиден — ничего вычеркивать не надо, такая строка будет искомой подпоследовательностью-палиндромом. Для последовательности из двух элементов [math]S(i, i + 1)[/math] возможны два варианта: если элементы равны, то мы имеем подпоследовательность-палиндром, ничего вычеркивать не надо. Если же элементы не равны, то вычеркиваем любой.

Пусть теперь нам дана подпоследовательность [math]S(i, j)[/math]. Если [math]S[i][/math] и [math]S[j][/math] элементы подпоследовательности не совпадают, то один из них нужно вычеркнуть. Тогда у нас останется подпоследовательность [math]S(i, j - 1)[/math] или [math]S(i + 1, j)[/math] — то есть мы сведем задачу к подзадаче: [math]L[i][j] = max(L[i][j - 1], L[i + 1][j])[/math]. Если же первый и последний элементы равны, то мы можем оставить оба, но необходимо знать решение задачи [math]S(i + 1, j - 1): L[i][j] = L[i + 1][j - 1] + 2[/math].

Асимптотика

Каждый элемент массива мы вычисляем 1 раз за [math]O(1)[/math] обращаясь к уже вычисленным элементам. Так как размер массива [math]n \times n[/math], то алгоритм работает за [math]O(n^2)[/math]

Пример

Рассмотрим решение на примере последовательности ABACCBA. Первым делом заполняем диагональ массива единицами, они будут соответствовать подпоследовательностями [math]S(i, i)[/math] из одного элемента. Затем начинаем рассматривать подпоследовательности длины два. Во всех подпоследовательностях, кроме [math]S(3, 4)[/math], элементы различны, поэтому в соответствующие ячейки запишем [math]1[/math], а в [math]L[3][4][/math][math]2[/math].

Получается, что мы будем заполнять массив по диагоналям, начиная с главной диагонали. Для подпоследовательностей длины [math]3[/math] получаются следующие значения: в подпоследовательности ABA первый и последний элемент равны, поэтому [math]L[0][2] = L[1][1] + 2[/math]. В остальных подпоследовательностях первый и последний элементы различны.

BAC: [math]L[1][3] = max(L[1][2], L[2][3]) = 1[/math]

ACC: [math]L[2][4] = max(L[2][3], L[3][4]) = 2[/math]

CCB: [math]L[3][5] = max(L[3][4], L[4][5]) = 2[/math]

CBA: [math]L[4][6] = max(L[4][5], L[5][6]) = 1[/math]

Продолжая далее аналогичные рассуждения, заполним все ячейки над диагональю и в ячейке [math]L[0][6][/math] получим ответ — [math]6[/math].

Если же в задаче необходимо вывести не длину, а саму подпоследовательность-палиндром, то дополнительно к массиву длин мы должны построить массив переходов — для каждой ячейки запомнить, какой из случаев был реализован.

Последовательность заполнения массива и массив переходов см. на изображениях ниже.

Заполнение массива длин (1) Заполнение массива длин (2) Заполнение массива длин (3) Заполнение массива длин (4) Массив переходов

Псевдокод

Перед вызовом процедуры заполняем [math]L[][][/math] начальными значениями: [math]L[i][j] = 1[/math] если [math]i=j[/math], [math]L[i][j] = 0[/math], если [math]i\gt j[/math], в остальных случаях [math]L[i][j]=-1[/math]. При первом вызове функции в качестве аргументов передаем индексы первого и последнего элементов исходной строки. Например для строки длиной [math] N [/math] вызов функции будет иметь следующий вид: [math]palSubSeq(0,N - 1)[/math]. Искомая же длина будет записана в ячейке [math]L[0][N-1][/math].

Функция для вычисления длины палиндрома ([math]left, right[/math] - границы исходной последовательности):

 palSubSeq(left, right)
   if L[left][right] == -1 
       if s[left] == s[right] 
           L[left][right] = palSubSeq(left + 1, right - 1) + 2
        else 
           L[left][right] = MAX(palSubSeq(left + 1, right), palSubSeq(left, right - 1))
   return L[left][right]

Функция для построения искомого палиндрома ([math]left, right[/math] — границы исходной последовательности, [math]palLeft=0, palRight=L[0][n-1][/math] — границы искомой):

 // palindrome — массив символов, где в palindrome[i] содержится символ искомой последовательности-палиндрома
 palChars(left, right, l1, l2)
   while left [math]\le[/math] right
     if left == right && L[left][right] == 1
       palindrome[palLeft++] = S[left++]
     else
       if S[left] == S[right]
         palindrome[palLeft++] = S[left++]
         palindrome[palRight--] = S[right--]
       else
         if L[left + 1][right] [math] \gt  [/math] L[left][right - 1]
           left++
         else
           right--

Литература

См. также