Как я могу найти LCS (самую длинную общую подпоследовательность) с ограничением пробела? - PullRequest
0 голосов
/ 16 апреля 2020

Я знаю общий алгоритм LCS и могу составить таблицу.

Но как найти LCS с ограничением , как показано на рисунке выше?

Как создать таблицу?

Пример LCS с ограничением на разрыв:

enter image description here

1 Ответ

0 голосов
/ 17 апреля 2020

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

За счет запоминания (т.е. кэширования) ответов вы получаете такую ​​же эффективность big-O, как и при Dynami c Programming. Он будет иметь больший постоянный коэффициент из-за использования вызовов функций вместо циклов for. Но они эквивалентны, и вы можете думать о DP как о рекурсии «наизнанку» с запоминанием.

Основная идея c состоит в том, чтобы создать «бюджет» для каждой последовательности, а затем учитывать это при пропуске писем. Существует 4 случая, в которых точно отслеживается стандартный рекурсивный подход к LCS.

  1. Завершить сопоставление. Возможно, это не лучший вариант, но он всегда доступен, если ни один из других параметров не удовлетворяет их условиям.
  2. Текущая пара символов совпадает. Работающий LCS может быть увеличен на 1, и бюджеты разрыва могут быть сброшены до максимального значения k.
  3. Есть еще некоторый бюджет, чтобы пропустить левый символ, поэтому попробуйте это. LCS не увеличивается, а бюджет левого промежутка уменьшается.
  4. То же, что 2, но теперь попробуйте пропустить нужный символ, если есть бюджет.

Вот некоторые Python код, который делает то, что вы хотите. Это можно сделать намного эффективнее, особенно если использовать язык лучше, чем Python, но это должно дать вам представление.

def gap_lcs(lh, rh, k): 
    ln, rn = len(lh), len(rh) 

    memo = {}
    choices = {}
    def rec(li, ri, l_budget, r_budget):
        # At the end of either sequence we are forced to use
        # Case 1: terminate the match.
        if li >= ln or ri >= rn:
            return 0 

        # Cache results. This limits the complexity to O(ln * lm * k^2).
        # Without this the recursion would take exponential time.
        key = (li, ri, l_budget, r_budget) 
        if key in memo: 
            return memo[key]

        # Case 1: terminate the match.
        res = 0
        choice = (0, 0)

        # Case 2: matching characters, extend the sequence.
        if lh[li] == rh[ri]:
            test = 1 + rec(li + 1, ri + 1, k, k)
            if test > res:
                res = test
                choice = (1, 1)

        # Case 3: skip the left character if there's still budget.
        if l_budget > 0:
            test = rec(li + 1, ri, l_budget - 1, r_budget)
            if test > res:
                res = test
                choice = (1, 0)

        # Case 4: skip the right character if there's still budget.
        if r_budget > 0:
            test = rec(li, ri + 1, l_budget, r_budget - 1)
            if test > res:
                res = test
                choice = (0, 1)

        memo[key] = res
        choices[key] = choice
        return res

    # Find the best combination of starting points within the two sequences.
    # This is so the gap constraint will not apply to skips at the start.
    res = 0
    best_li, best_ri = 0, 0
    for li in range(ln):
        for ri in range(rn):
            test = rec(li, ri, k, k)
            if test > res:
                res, best_li, best_ri = test, li, ri

    # Reconstruct the LCS by following the choices we tracked,
    # starting from the best start we found.
    li, ri = best_li, best_ri
    l_budget, r_budget = k, k

    path = []
    while True:
        key = (li, ri, l_budget, r_budget)

        # Case 1.
        if key not in choices:
            break

        inc_li, inc_ri = choices[key]

        # Case 1.
        if inc_li == 0 and inc_ri == 0:
            break

        if inc_li == 1 and inc_ri == 1:
            # Case 2.
            l_budget, r_budget = k, k
            path.append((lh[li], li, ri))
        else:
            # Cases 3 and 4.
            l_budget -= inc_li
            r_budget -= inc_ri

        li += inc_li
        ri += inc_ri

    return path

Это даст желаемый результат для ваших примеров.

gap_lcs('RCLPCRR', 'RPPLCPLRC', 2)
# [('R', 0, 0), ('P', 3, 1), ('C', 4, 4), ('R', 6, 7)]

gap_lcs('RCLPCRR', 'RPPLCPLRC', 1)
# [('C', 1, 4), ('P', 3, 5), ('R', 5, 7)]

gap_lcs('RCLPCRR', 'RPPLCPLRC', 0)
# [('R', 0, 7), ('C', 1, 8)]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...