Самая длинная повторяющаяся (k раз) подстрока

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

Это для проблемы проекта Rosalind LREP. Я пытаюсь найти в строке самую длинную подстроку, состоящую из k, и мне предоставили дерево суффиксов, что очень удобно. Я знаю, что мне нужно аннотировать таблицу суффиксов количеством листьев-потомков от каждого узла, затем найти узлы с >=k потомками и, наконец, найти самый глубокий из этих узлов. Теоретически я готов.

Мне очень помогли следующие ресурсы (к сожалению, я могу опубликовать только 2):

Я могу получить пути от корня к каждому листу, но я не могу понять, как предварительно обработать дерево таким образом, чтобы я мог получить количество потомков от каждого узла. У меня есть отдельный алгоритм, который работает с небольшими последовательностями, но он имеет экспоненциальную сложность, поэтому для больших вещей он занимает слишком много времени. Я знаю, что с DFS я смогу выполнить всю задачу линейной сложности. Чтобы этот алгоритм работал, мне нужно иметь возможность получить самый длинный k-торф длиной ~ 40 000 строк менее чем за 5 минут.

Вот некоторые примеры данных (первая строка: sequence, вторая строка: k, формат таблицы суффиксов: parent child location length):

CATACATAC$
2
1 2 1 1
1 7 2 1
1 14 3 3
1 17 10 1
2 3 2 4
2 6 10 1
3 4 6 5
3 5 10 1
7 8 3 3
7 11 5 1
8 9 6 5
8 10 10 1
11 12 6 5
11 13 10 1
14 15 6 5
14 16 10 1

Результатом этого должно быть CATAC.

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

#authors listed at
#http://en.literateprograms.org/Depth-first_search_(Python)?action=history&offset=20081013235803
class Vertex:
    def __init__(self, data):
        self.data = data
        self.successors = []

def depthFirstSearch(start, isGoal, result):
    if start in result:
        return False

    result.append(start)

    if isGoal(start):
        return True
    for v in start.successors:
        if depthFirstSearch(v, isGoal, result):
            return True

    # No path was found
    result.pop()
    return False

def lrep(seq,reps,tree):
    n = 2 * len(seq) - 1
    v = [Vertex(i) for i in xrange(n)]
    edges = [(int(x[0]),int(x[1])) for x in tree]
    for a, b in edges:
        v[a].successors.append(v[b])

    paths = {}
    for x in v:
        result = []
        paths[x.data] = []
        if depthFirstSearch(v[1], (lambda v: v.data == x.data), result):
            path = [u.data for u in result]
            paths[x.data] = path

Что я хотел бы сделать, так это предварительно обработать дерево, чтобы найти узлы, которые удовлетворяют требованию descendants >= k, прежде чем находить глубину. Я еще даже не дошел до того, как буду вычислять глубину. Хотя я предполагаю, что у меня будет словарь, чтобы отслеживать глубину каждого узла в пути, а затем суммировать.

Итак, мой первый и самый важный вопрос: "Как выполнить предварительную обработку дерева с потомками листьев?"

Мой второй менее важный вопрос: "Как после этого я могу быстро вычислить глубину?"

P.S. Я должен заявить, что это не домашнее задание или что-то в этом роде. Я всего лишь биохимик, пытающийся расширить свой кругозор с помощью некоторых вычислительных задач.


person Gambrinus    schedule 09.11.2012    source источник
comment
По мере того, как я углубляюсь в это, я понимаю, что мне дан список ребер, а не само дерево. Поэтому мне нужно сгенерировать дерево из ребер, аннотируя потомками листья в каждом узле. Я просто не знаю, как даже начать.   -  person Gambrinus    schedule 10.11.2012
comment
вы также можете спросить biostars.org.   -  person Pierre    schedule 14.11.2012


Ответы (1)


Хороший вопрос для упражнения в основных операциях со строками. Я больше не помнил дерево суффиксов ;) Но, как вы сказали: теоретически, вы настроены.

Как предварительно обработать дерево с потомками листьев?

заглушка википедии на эту тему немного сбивает с толку. Вам нужно только знать, являетесь ли вы самым внешним нелистовым узлом с n >= k дочерними элементами. Если вы нашли подстроку от корневого узла до этой во всей строке, суффиксное дерево сообщает вам, что существует n возможных продолжений. Таким образом, должно быть n мест, где встречается эта строка.

После этого, как я могу быстро вычислить глубину?

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

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

  1. У узла нет дочерних элементов. Это листовой узел, результат недействителен.
  2. Каждый дочерний элемент возвращает недопустимый результат. Это последний нелистовой узел, результат равен нулю (после этого узла больше нет символов). Если этот узел имеет n дочерних элемента, объединенная строка каждого ребра от корня до этого узла появляется n раз во всей строке. Если нам нужно хотя бы k узлов и k > n, результат тоже недействителен.
  3. Один или несколько листьев возвращают что-то действительное. Результатом является максимум возвращаемого значения плюс длина строки, присоединяющей к нему ребро.

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

Код

Сначала вы должны попытаться закодировать это самостоятельно. Построение дерева простое, но не тривиальное, если вы хотите собрать всю необходимую информацию. Тем не менее вот простой пример. Обратите внимание: каждая проверка работоспособности отбрасывается, и все будет ужасно терпеть неудачу, если ввод каким-то образом недействителен. Например. не пытайтесь использовать какой-либо другой корневой индекс, кроме одного, не ссылайтесь на узлы как на родительские, на которые раньше не ссылались как на дочерние, и т. д. Много возможностей для улучшения *совет;)* .

class Node(object):
    def __init__(self, idx):
        self.idx = idx     # not needed but nice for prints 
        self.parent = None # edge to parent or None
        self.childs = []   # list of edges

    def get_deepest(self, k = 2):
        max_value = -1
        max_node = None
        for edge in self.childs:
            r = edge.n2.get_deepest()
            if r is None: continue # leaf
            value, node = r
            value += len(edge.s)
            if value > max_value: # new best result
                max_value = value
                max_node = node
        if max_node is None:
            # we are either a leaf (no edge connected) or 
            # the last non-leaf.
            # The number of childs have to be k to be valid.
            return (0, self) if len(self.childs) == k else None
        else:
            return (max_value, max_node)

    def get_string_to_root(self):
        if self.parent is None: return "" 
        return self.parent.n1.get_string_to_root() + self.parent.s

class Edge(object):
    # creating the edge also sets the correspondending
    # values in the nodes
    def __init__(self, n1, n2, s):
        #print "Edge %d -> %d [ %s]" % (n1.idx, n2.idx, s)
        self.n1, self.n2, self.s = n1, n2, s
        n1.childs.append(self)
        n2.parent = self

nodes = {1 : Node(1)} # root-node
string = sys.stdin.readline()
k = int(sys.stdin.readline())
for line in sys.stdin:
    parent_idx, child_idx, start, length = [int(x) for x in line.split()]
    s = string[start-1:start-1+length]
    # every edge constructs a Node
    nodes[child_idx] = Node(child_idx)
    Edge(nodes[parent_idx], nodes[child_idx], s)

(depth, node) = nodes[1].get_deepest(k)
print node.get_string_to_root()
person Peter Schneider    schedule 16.11.2012