Найдите самый длинный путь с помощью BFS

У меня есть список из 794 длинных слов из трех букв. Моя задача — найти слово (слова) с самым длинным путем к заданному слову.

Определение (дочерние элементы).
Дочерними элементами родительского слова являются родительские слова с заменой одной и только одной буквы.

Пример:
"can", "run", "rap" и т. д. являются дочерними элементами слова "ran" (учитывая, что эти слова существуют в списке).

Определение (путь):
Путь — это последовательность слов, в которой каждое слово создается путем замены одной буквы в предыдущем.

#Pseudo code
Given a word 
    Put the word in a queue
    Keep a list with all visited words
    As long as the queue is not empty
        Get the first word in the queue
        Generate a list with all the children to this word
        For every children in that list
            If the child is not in the list of visited words
                Append the child to the list of visited words
                Put the child in the queue
    Print the path to the last grandchild.

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

Вопрос.
Верна ли моя идея? Как я могу проверить, действительно ли это работает?


Фактический код можно посмотреть ниже, но без комментариев он может быть бессмысленным.

Редактировать
Поскольку деревья и списки могут быть немного медленными, я заменил их наборами.

from Queue import Queuenode; import Bintree
with open('word3',encoding='utf-8') as f:
    lista=f.readlines()
    lista=[x.strip('\n') for x in lista]
alfabet='abcdefghijklmnopqrstuvwxyzåäö'
class Word:
    def __init__(self, w, f = None):
        self.word = w
        self.parent = f
children=Bintree.Bintree()
for i in lista:
    children.put(i)
def barn(far):
    barnlista=[]
    for i in range(3):
        for j in range(29):
            empty=''
            empty+=far[0:i]+alfabet[j]+far[i+1:]
            if empty!=far and children._exists(empty):
                barnlista+=[empty]
    return barnlista
ko=Queuenode()
def vag(item):
    start=item
    counter=0
    while start!=None:
        counter+=1
        print(start.word)
        start=start.parent
    print(counter)
def bfsx(startord):
    ko.put(Word(startord))
    besokta=[startord]
    while not ko.isempty():
        first=ko.get()
        lista=barn(first.word)
        for child in lista:
            if child not in besokta:
                besokta+=[child]
                ko.put(Word(child,first))
    vag(first)

person Lozansky    schedule 19.09.2016    source источник


Ответы (1)


IIUC, это не гарантирует работу (на самом деле вы можете создавать случаи, когда это не так).

Предположим, вы начинаете с узла a; есть прямой путь a b; есть также прямой путь a c и непрямой путь c b.

Предположим, что при переборе дочерних элементов a вы сталкиваетесь с b до c. Вы имеете дело с b и отмечаете его как посещенный. В какой-то момент позже вы сталкиваетесь с c и, в конце концов, снова пересматриваете b. Однако в этот момент b уже считается посещенным, поэтому ваш алгоритм будет рассматривать более короткий подпуть a b, а не более длинный a c b.

Вы также не можете избавиться от пометки «посещено», так как история вашего графика ясно дает понять, что это не DAG. Если вы удалите логику «посещений», вы столкнетесь с бесконечными циклами.

person Ami Tavory    schedule 19.09.2016
comment
Кажется, это верный момент. Я обновлю с новой идеей. - person Lozansky; 19.09.2016
comment
На самом деле, я думаю, что ваша точка зрения может быть недействительной. Это потому, что когда мы генерируем дочерние элементы для a (т.е. b и c), нам нужно отслеживать их, чтобы не зацикливаться. Таким образом, когда b и c были сгенерированы, у c не должно быть возможности сгенерировать b как дочерний элемент. - person Lozansky; 19.09.2016
comment
@Lozansky Я не слежу за твоей точкой зрения, или, скорее, это именно то, что я пытаюсь донести. Предположим, кто-то укажет вам самый длинный путь в графе, и это будет a->c->b->d. Затем, оглядываясь назад, когда ваш алгоритм обнаружит b в качестве первого потомка a, будет лучше пропустить его и подождать, пока он не будет достигнут из c. Проблема в том, что алгоритм этого не знает - он пометит b как посещенный и не согласится рассматривать этот путь. Если я что-то упустил, не стесняйтесь указывать на это. - person Ami Tavory; 19.09.2016
comment
Я думаю, что понимаю ваши рассуждения, но мне трудно реализовать эту идею на практике. Трудная часть, кажется, заключается в том, когда добавить слово в набор посещенных слов. Буду очень признателен за небольшое руководство. - person Lozansky; 19.09.2016
comment
@Lozansky Я не знаю, как это решить - я не скрываю никакой информации. - person Ami Tavory; 19.09.2016
comment
Хорошо, спасибо за вашу помощь! Я попрошу попытаться получить разъяснения по этой проблеме завтра, и если я ее решу, и если вы заинтересованы, я пришлю вам решение. - person Lozansky; 19.09.2016
comment
@ Лозански Конечно. Таким образом, если вы найдете решение, вы можете написать его как ответ (нет проблем с ответом на ваш собственный вопрос). Люди, заинтересованные в вопросе, могут отметить его, и тогда они увидят изменения. - person Ami Tavory; 19.09.2016
comment
Мое решение было на самом деле правильным, поскольку они хотели, чтобы мы нашли кратчайший путь между двумя узлами (если это имеет смысл). Извините за путаницу! - person Lozansky; 20.09.2016
comment
@Lozansky О, рад, что ты в конце концов с этим справился. FWIW, я думаю, что другая интерпретация проблемы довольно сложна. Всего наилучшего. - person Ami Tavory; 20.09.2016