Решая задачу о самой длинной повторяющейся подпоследовательности с помощью восходящего динамического программирования, я начал сталкиваться с крайним случаем, когда буква повторялась нечетное число раз.
Цель состоит в том, чтобы найти самую длинную подпоследовательность, которая встречается в строке дважды, используя элементы с разными индексами. Диапазоны могут перекрываться, но индексы должны быть непересекающимися (т. е. str[1], str[4] и str[2], str[6] могут быть решением, но не str[1], str[2] и str[2], str[3]).
Обновлено: Неправильно понятый пример. (Смотрите комментарий)
s = 'AXXXA'
n = len(s)
dp = [['' for i in range(n + 1)] for j in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, n + 1):
if (i != j and s[i - 1] == s[j - 1]):
dp[i][j] = dp[i - 1][j - 1] + s[i - 1]
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
print(dp[n][n])
Есть какие-нибудь указания, как этого избежать?
При вводе s = 'AXXXA' ответ должен быть либо A, либо X, но окончательный результат возвращает XX, по-видимому, объединяя третий X как с первым X, так и со вторым X.
Я не хочу добавлять проверку соответствия (s[i - 1] == s[j - 1]), чтобы увидеть, есть ли s[i - 1] in dp[i - 1][j - 1], потому что другой ввод может быть чем-то вроде AAJDDAJJTATA, который должен добавить A дважды.
Я пытался решить задачу из книги, и пример был моим собственным, основанным на том, что я понял из непересекающегося. Как указывает принятый ответ, это не понимание непересекаемости в LRS. (Я не осознавал, что должен был внести здесь правку. Извините, если я вызвал некоторую путаницу.)
Спасибо за ответ. Я все еще сильно подозреваю, что, даже упомянув слово «непересекающийся», книжная проблема действительно предполагала, что проблема будет такой, как вы ее интерпретировали. Сама книга вносит ясность? Потому что, что интересно, оказывается, что проблема, как вы интерпретировали, на самом деле NP-полна, даже если мы ограничим ее требованием, чтобы каждый символ принадлежал одной из двух «половинок», поэтому она почти наверняка не разрешима за O (n ^2) время: cs.stackexchange.com/a/130796/1984






Для получения самого длинного значения лучше всего реализовать новую функцию с результатом dp сетки.
Ваш алгоритм в порядке, вам нужно только увеличить новый dp на 1, когда s[i - 1] == s[j - 1]:
n = len(s)
dp = [[0 for _ in range(n + 1)] for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, n + 1):
if s[i - 1] == s[j - 1] and i != j:
dp[i][j] = 1 + dp[i - 1][j - 1]
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
return dp[-1][-1]
Если вы хотите построить самый длинный, вы можете использовать сетку dp и всякий раз, когда if s[i - 1] == s[j - 1] and i != j удовлетворяет, добавьте символ к самому длинному:
def get_longest():
i, j = n, n
lrs = []
while i > 0 and j > 0:
if s[i - 1] == s[j - 1] and i != j:
lrs.append(s[i - 1])
i -= 1
j -= 1
elif dp[i - 1][j] > dp[i][j - 1]:
i -= 1
else:
j -= 1
return ''.join(lrs[::-1])
def LRS(s):
def get_longest():
i, j = n, n
lrs = []
while i > 0 and j > 0:
if s[i - 1] == s[j - 1] and i != j:
lrs.append(s[i - 1])
i -= 1
j -= 1
elif dp[i - 1][j] > dp[i][j - 1]:
i -= 1
else:
j -= 1
return ''.join(lrs[::-1])
n = len(s)
dp = [[0 for _ in range(n + 1)] for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, n + 1):
if s[i - 1] == s[j - 1] and i != j:
dp[i][j] = 1 + dp[i - 1][j - 1]
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
print(get_longest())
return dp[-1][-1]
s = 'AXXXA'
print(LRS(s))
ХХ 2
Этот ответ на самом деле правильный. См. мое объяснение ниже (или выше, в зависимости от количества голосов).
Вы можете отслеживать индексы последних добавленных символов и следить за тем, чтобы, когда два символа одинаковы, их индексы должны отличаться не только друг от друга, но и от индекса последнего добавленного символа:
s = 'AXXXA'
n = len(s)
dp = [[('', 0, 0) for i in range(n + 1)] for j in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, n + 1):
last = dp[i - 1][j - 1]
if last[2] != i != j != last[1] and s[i - 1] == s[j - 1]:
dp[i][j] = last[0] + s[i - 1], i, j
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1], key=lambda t: t[0])
print(dp[n][n][0]) # outputs X
Демо: https://ideone.com/mSKgKy
Я думаю, вы имели в виду key=lambda t: len(t[0]), поскольку, например, max('abc', 'de') == 'de'. Но это не самая серьезная проблема: даже после исправления ошибки при вводе AAAAA вместо AAA выдается AA: ideone.com/iMvV0F.
Проблема в том, что, поскольку вы отслеживаете только две конечные точки (а не все другие используемые индексы), вы можете ошибочно разрешить повторное использование индексов второй строкой, если они «далеко от кончика» первой.
Отдельно я думаю, что этот алгоритм также не работает и в другом направлении (слишком пессимистично в отношении некоторых входных данных), хотя я еще не нашел примера для этого: поскольку для каждого (i, j) может быть несколько одинаково хороших пар конечных положений, но вы отслеживайте только одну такую пару; если отслеживаемая пара сталкивается с конфликтом, вы пессимистично предполагаете, что ни одна пара подпоследовательностей не может быть расширена (хотя возможно, что это может быть какая-то другая одинаково хорошая пара подпоследовательностей, заканчивающаяся в разных позициях).
На самом деле ваш первоначальный алгоритм и его ответ верны (... но это хороший вопрос, потому что другие могут запутаться в том, что означает LRS).
Учитывая ваш ввод (in), подпоследовательности (s1, s2):
in: AXXXA
s1: XX
s2: XX
Итак, XX (длина 2) здесь действительно правильный ответ.
X будет правильным ответом для непересекающейся версии задачи, где диапазоны, а не только отдельные индексы, должны быть непересекающимися.
Не знаю, почему автор это принял... их собственные критерии «индексы должны быть непересекающимися» («но не str[1], str[2] и str[2], str[3]») специально запрещают именно этот сценарий. Хотя «непересекающийся» без каких-либо дополнительных уточнений действительно может означать несколько вещей, тот факт, что они привели этот пример недопустимого решения, означает, что они намереваются сделать все используемые индексы непересекающимися.
Спасибо за указание на это. Описание проблемы LRS меня сбило с толку, и я задумался о неправильности ожидаемой доходности.
Почему вы приняли ответ, который прямо противоречит вашему требованию «индексы должны быть непересекающимися»?