PassFront
  1. PassFront
  2. База знаний
  3. Рекурсия, жадные алгоритмы и ДП

Как найти наибольшую общую подпоследовательность (LCS)

Рекурсия, жадные алгоритмы и ДП

`dp[i][j]` — длина LCS префиксов `a[:i]` и `b[:j]`: при совпадении символов берём `dp[i−1][j−1] + 1`, иначе `max(dp[i−1][j], dp[i][j−1])`. Время и память `O(n·m)`.

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

Идём от dp[n][m]: если a[i−1] == b[j−1], берём символ и шагаем по диагонали, иначе переходим в соседнюю клетку (dp[i−1][j] или dp[i][j−1]) с большим значением. Символы собираются в обратном порядке.

Для "ABCBDAB" и "BDCABA" длина LCS — 4; ответов несколько, например "BCBA".

Оптимизации и нюансы

  • Только длина — хватает двух строк таблицы: O(min(n, m)) памяти.
  • Восстановление с линейной памятью — алгоритм Хиршберга (D&C поверх того же ДП).
  • Подпоследовательность ≠ подстрока: для наибольшей общей подстроки dp[i][j] при несовпадении обнуляется, а ответ — максимум по таблице.

Где встречается

diff и системы контроля версий (Git по умолчанию использует алгоритм Майерса O((n+m)·D), где D — размер правки), сравнение ДНК-последовательностей.

Коротко для собеседования

LCS считают двумерной ДП по префиксам двух строк: совпавший символ продлевает ответ по диагонали, иначе берём лучшего из соседей. Не путайте её с общей подстрокой, где при несовпадении значение обнуляется; на LCS построены diff и расстояние редактирования без замен.

Другие вопросы раздела