Общее число палиндромных подпоследовательностей в строке

вопрос такой--

для каждой строки, заданной в качестве входных данных, вам нужно указать количество подпоследовательностей, которые являются палиндромами (необязательно должны быть разными). Обратите внимание, что пустая строка не является палиндромом. Например, палиндромные подпоследовательности "aab":

"a", "a", "b", "aa", и метод возвращает 4.

У меня было решение динамического программирования для поиска самой длинной палиндромной подпоследовательности и поэтому я попытался берите из него идеи. Не мог найти решение. Может быть, динамическое программирование даже не требуется. Предложения, пожалуйста.

и есть еще одна загвоздка. Когда условие "необязательно должно быть отличным" удалено, можем ли мы все еще считать, фактически не генерируя все палиндромные подпоследовательности?

3 ответов


[EDIT 19/10/2015: анонимный рецензент указал на проблему с формулой, которая побудила меня заметить еще одну, еще большую ошибку... Исправлено.]

теперь я вижу, как уменьшить время решения до O (n^2). Я оставлю свой другой ответ на случай, если он интересен как ступенька к этому. Примечание: это (также) только решение первой части проблемы; я не вижу способа эффективно рассчитывать только distinct палиндромные последовательности (ПС).

вместо того, чтобы считать количество PS, которые начинаются и заканчиваются точно в позициях i и j, давайте посчитаем, сколько начинается в или после Я и заканчивается на или перед j. Назовем это g (i, j).

мы можем попытаться написать g(i, j) = g(i, j-1) + g (i+1, j) + (x[i] == x[j])*g (i+1, j-1) для случая, когда j > i. Но это не совсем работает, потому что первые два члена будут дважды считать любые PS, которые начинаются после i и конец до j.

ключевое понимание состоит в том, чтобы заметить, что мы можем легко вычислить количество PS, которые начинаются или заканчиваются в некоторых точно позиция путем вычитания других значений g() и, возможно, добавления еще больше значений g () обратно, чтобы компенсировать двойной подсчет. Например, количество PS, которые начинаются на ровно Я и заканчивается на ровно j - g (i, j) - g(i+1, j) - g(i, j-1) + g (i+1, j-1): последний член исправляет для дело в том, что и второй, и третий члены подсчитывают все g(i+1, j-1) PS, которые начинаются после Я и конец до j.

каждый PS, который начинается На или после i и заканчивается на или до j, находится ровно в 1 из 4 категорий:

  1. он начинается после i и заканчивается до j.
  2. он начинается на i и заканчивается до j.
  3. он начинается после i и заканчивается на j.
  4. он начинается на i и заканчивается на j.

g(i+1, к) подсчитывает все ПС в категорию 1 или 3, и G(Я, J-1) подсчитывает все ПС в категории 1 или 2, так что их сумма г(я+1, к) + Г(Я, J-1) подсчитывает все ПС в категории 2 или 3 раза каждый, и все P. S. В 1 разряд в два раза. Поскольку g(i+1, j-1) считает все PS только в категории 1, вычитая это, чтобы получить g(i+1, j) + g(i, j-1) - g (i+1, j-1) дает общее количество PS в категории 1, 2 и 3. Остальные ПС относятся к категории 4. Если x[i]!= x[j] тогда в этой категории нет PS; в противном случае, точно так же, как многие, как есть PS, которые начинаются на или после i+1 и заканчиваются на или до j-1, а именно g(i+1, j-1), плюс один дополнительный для 2-символьной последовательности x[i]x[j]. [EDIT: спасибо комментатору Tuxdude за 2 исправления здесь!]

С этим в руке мы можем выразить g() таким образом, что изменяет квадратичный случай с f () на постоянное время:

g(i, i) = 1 (i.e. when j = i)
g(i, i+1) = 2 + (x[i] == x[i+1]) (i.e. 3 iff adjacent chars are identical, otherwise 2)
g(i, j) = 0 when j < i (this new boundary case is needed)
g(i, j) = g(i+1, j) + g(i, j-1) - g(i+1, j-1) + (x[i] == x[j])*(g(i+1, j-1)+1) when j >= i+2

окончательный ответ теперь просто g (1, n).


вот ужасное решение O(n^4):

каждый подпоследовательность-палиндром начинает в какой-то позиции i и заканчивается на некоторые позиции j >= i такой, что X[Я] = х[J], и ее "интерьер" (все символы кроме первого и последнего) либо пусто, либо палиндромную последовательность х[я+1 .. j-1].

таким образом, мы можем определить f(i, j) как число палиндромных подпоследовательностей, начинающихся с i и заканчивающихся на j >= i. Тогда

f(i, j) = 0 if x[i] != x[j]
f(i, i) = 1 (i.e. when j = i)
f(i, j) = 1 + the sum of f(i', j') over all i < i' <= j' < j otherwise

[EDIT: исправлено количество палиндромных подпоследовательности длины

тогда окончательный ответ-сумма f (i, j) над всеми 1

DP для этого O(n^4), потому что есть N^2 записей таблицы, и вычисление каждого из них занимает O (n^2) времени. (Вероятно, можно ускорить это, по крайней мере, до O(n^3), используя тот факт, что x[i] != x[j] означает f (i, j) = 0.)


интуитивное решение O (n^3) с использованием DP:

пусть каждое состояние dp (i,j) представляет количество палиндромных подпоследовательностей в строке[i...Дж] Тогда простая рекурсивная формула

for k in range i, j-1:
    if(A[j]==A[k]){
        dp(i,j) = dp(i,j) + dp(k+1,j-1);

идея очень простая.. Для добавления нового символа проверьте, является ли он концом подпоследовательности или нет. Если в ранее вычисленной меньшей подзадаче существует один и тот же символ, то он добавляет количество подпоследовательностей,содержащихся в диапазоне (k+1, j-1). Просто позаботьтесь о угловых случаях. Добавить один как недавно добавленный символ также является подпоследовательностью одного символа. Даже если в диапазоне нет подпоследовательностей (k+1 , j-1), вы все равно получите 1 новую подпоследовательность длины 2 (например, "aa").