Самая длинная повторяющаяся подпоследовательность: крайние случаи

Проблема

Решая задачу о самой длинной повторяющейся подпоследовательности с помощью восходящего динамического программирования, я начал сталкиваться с крайним случаем, когда буква повторялась нечетное число раз.

Цель состоит в том, чтобы найти самую длинную подпоследовательность, которая встречается в строке дважды, используя элементы с разными индексами. Диапазоны могут перекрываться, но индексы должны быть непересекающимися (т. е. 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 дважды.

Почему вы приняли ответ, который прямо противоречит вашему требованию «индексы должны быть непересекающимися»?

j_random_hacker 23.05.2024 05:56

Я пытался решить задачу из книги, и пример был моим собственным, основанным на том, что я понял из непересекающегося. Как указывает принятый ответ, это не понимание непересекаемости в LRS. (Я не осознавал, что должен был внести здесь правку. Извините, если я вызвал некоторую путаницу.)

William Edwardson 27.05.2024 17:40

Спасибо за ответ. Я все еще сильно подозреваю, что, даже упомянув слово «непересекающийся», книжная проблема действительно предполагала, что проблема будет такой, как вы ее интерпретировали. Сама книга вносит ясность? Потому что, что интересно, оказывается, что проблема, как вы интерпретировали, на самом деле NP-полна, даже если мы ограничим ее требованием, чтобы каждый символ принадлежал одной из двух «половинок», поэтому она почти наверняка не разрешима за O (n ^2) время: cs.stackexchange.com/a/130796/1984

j_random_hacker 06.06.2024 05:16
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
3
159
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

  • Для получения самого длинного значения лучше всего реализовать новую функцию с результатом 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

Этот ответ на самом деле правильный. См. мое объяснение ниже (или выше, в зависимости от количества голосов).

Nathan Davis 22.05.2024 07:07

Вы можете отслеживать индексы последних добавленных символов и следить за тем, чтобы, когда два символа одинаковы, их индексы должны отличаться не только друг от друга, но и от индекса последнего добавленного символа:

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.

j_random_hacker 23.05.2024 05:55

Проблема в том, что, поскольку вы отслеживаете только две конечные точки (а не все другие используемые индексы), вы можете ошибочно разрешить повторное использование индексов второй строкой, если они «далеко от кончика» первой.

j_random_hacker 23.05.2024 06:00

Отдельно я думаю, что этот алгоритм также не работает и в другом направлении (слишком пессимистично в отношении некоторых входных данных), хотя я еще не нашел примера для этого: поскольку для каждого (i, j) может быть несколько одинаково хороших пар конечных положений, но вы отслеживайте только одну такую ​​пару; если отслеживаемая пара сталкивается с конфликтом, вы пессимистично предполагаете, что ни одна пара подпоследовательностей не может быть расширена (хотя возможно, что это может быть какая-то другая одинаково хорошая пара подпоследовательностей, заканчивающаяся в разных позициях).

j_random_hacker 23.05.2024 06:02
Ответ принят как подходящий

На самом деле ваш первоначальный алгоритм и его ответ верны (... но это хороший вопрос, потому что другие могут запутаться в том, что означает LRS).

Учитывая ваш ввод (in), подпоследовательности (s1, s2):

in: AXXXA
s1:  XX
s2:   XX

Итак, XX (длина 2) здесь действительно правильный ответ.

X будет правильным ответом для непересекающейся версии задачи, где диапазоны, а не только отдельные индексы, должны быть непересекающимися.

Не знаю, почему автор это принял... их собственные критерии «индексы должны быть непересекающимися» («но не str[1], str[2] и str[2], str[3]») специально запрещают именно этот сценарий. Хотя «непересекающийся» без каких-либо дополнительных уточнений действительно может означать несколько вещей, тот факт, что они привели этот пример недопустимого решения, означает, что они намереваются сделать все используемые индексы непересекающимися.

j_random_hacker 23.05.2024 04:33

Спасибо за указание на это. Описание проблемы LRS меня сбило с толку, и я задумался о неправильности ожидаемой доходности.

Nathan Davis 27.05.2024 02:37

Другие вопросы по теме