Python文字游戏。第一个单词的最后一个字母==第二个单词的第一个字母。找到最长的单词序列

时间:2014-12-22 22:17:57

标签: python

我试图编写一个模仿文字游戏的程序,从一组给定的单词中,它会找到最长的单词序列。没有任何一个词可以使用两次。

我可以完成匹配的字母和单词,并将它们存储到列表中,但是我无法理解如何处理列表中潜在指数的单词可能性。如果单词1与单词2匹配然后我沿着那条路走下去,那么我如何备份以查看单词3或4是否与单词1匹配然后开始他们自己的路由,所有这些都来自第一个单词?

我在考虑某种方式调用内部函数可能吗?

我知道它无法完成我需要做的事情,但这只是一个开始。在此先感谢您的帮助!

g = "audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus"

def pokemon():
    count = 1
    names = g.split()
    first = names[count]
    master = []
    for i in names:
        print (i, first, i[0], first[-1])
        if i[0] == first[-1] and i not in master:
            master.append(i)
            count += 1
            first = i
            print ("success", master)
    if len(master) == 0:
        return "Pokemon", first, "does not work"
    count += 1
    first = names[count]

pokemon()

4 个答案:

答案 0 :(得分:3)

你在内部调用函数的想法很好。我们可以通过递归来解决这个问题:

def get_neighbors(word, choices):
    return set(x for x in choices if x[0] == word[-1])

def longest_path_from(word, choices):
    choices = choices - set([word])
    neighbors = get_neighbors(word, choices)

    if neighbors:
        paths = (longest_path_from(w, choices) for w in neighbors)
        max_path = max(paths, key=len)
    else:
        max_path = []

    return [word] + max_path

def longest_path(choices):
    return max((longest_path_from(w, choices) for w in choices), key=len)

现在我们只定义单词列表:

words = ("audino bagon baltoy banette bidoof braviary bronzor carracosta "
         "charmeleon cresselia croagunk darmanitan deino emboar emolga "
         "exeggcute gabite girafarig gulpin haxorus")

words = frozenset(words.split())

使用一组字词致电longest_path

>>> longest_path(words)
['girafarig', 'gabite', 'exeggcute', 'emolga', 'audino']

要了解的一些事项:正如您所指出的,这具有指数级的复杂性,所以要小心!另外,知道python有一个递归限制!

答案 1 :(得分:2)

使用一些黑魔法和图论我发现了一个可能很好的部分解决方案(未经过彻底测试)。

我们的想法是将您的问题映射到图形问题而不是简单的迭代问题(尽管它也可能有用!)。所以我将图形的节点定义为单词的第一个字母和最后一个字母。我只能在firstlast类型的节点之间创建边。我无法将节点first数字X映射到节点last数字X(单词不能自己跟随)。从那以后你的问题就像the Longest path problem一样,在一般情况下往往是NP难的:)

在此处提供一些信息:stackoverflow-17985202我设法写了这个:

g = "audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus"
words = g.split()
begin = [w[0] for w in words]  # Nodes first
end = [w[-1] for w in words]  # Nodes last

links = []
for i, l in enumerate(end):  # Construct edges
    ok = True
    offset = 0
    while ok:
        try:
            bl = begin.index(l, offset)
            if i != bl:  # Cannot map to self
                links.append((i, bl))
            offset = bl + 1  # next possible edge
        except ValueError:  # no more possible edge for this last node, Next!
            ok = False

# Great function shamelessly taken from stackoverflow (link provided above)
import networkx as nx
def longest_path(G):
    dist = {} # stores [node, distance] pair
    for node in nx.topological_sort(G):
        # pairs of dist,node for all incoming edges
        pairs = [(dist[v][0]+1,v) for v in G.pred[node]]
        if pairs:
            dist[node] = max(pairs)
        else:
            dist[node] = (0, node)
    node,(length,_)  = max(dist.items(), key=lambda x:x[1])
    path = []
    while length > 0:
        path.append(node)
        length,node = dist[node]
    return list(reversed(path))

# Construct graph
G = nx.DiGraph()
G.add_edges_from(links)
# TADAAAA!
print(longest_path(G))

虽然它看起来不错,但有一个缺点。您的示例有效,因为输入字的结果图中没有循环,但是,此解决方案在循环图上失败。 解决这个问题的方法是检测循环并打破它们。检测可以这样做:

if nx.recursive_simple_cycles(G):
    print("CYCLES!!! /o\")

可以通过在循环中删除随机边缘来完成循环,然后您将随机找到问题的最佳解决方案(想象一个带尾部的循环,您应该在具有3个边缘的节点上切割循环)因此,我建议通过尝试所有可能的循环中断,计算最长路径并采用最长路径中的最长路径来强制执行此部分。如果你有多个循环,那么它的可能性就会变得更具爆炸性......但是它是NP难的,至少我看到它的方式并且我现在不打算解决这个问题:)

希望有所帮助

答案 2 :(得分:0)

这是一个不需要递归的解决方案。它使用itertools permutation function来查看单词的所有可能排序,并找到长度最长的单词。为了节省时间,一旦订单点击不起作用的单词,它就会停止检查排序并继续前进。

>>> g = 'girafarig eudino exeggcute omolga gabite'
... p = itertools.permutations(g.split())
... longestword = ""
... for words in p:
...     thistry = words[0]
...     # Concatenates words until the next word doesn't link with this one.
...     for i in range(len(words) - 1):
...         if words[i][-1] != words[i+1][0]:
...             break
...         thistry += words[i+1]
...         i += 1
...     if len(thistry) > len(longestword):
...         longestword = thistry
...         print(longestword)
... print("Final answer is {}".format(longestword))
girafarig
girafariggabiteeudino
girafariggabiteeudinoomolga
girafariggabiteexeggcuteeudinoomolga
Final answer is girafariggabiteexeggcuteeudinoomolga

答案 3 :(得分:0)

首先,让我们看看问题是什么:

from collections import defaultdict
import pydot

words = (
    "audino bagon baltoy banette bidoof braviary bronzor carracosta "
    "charmeleon cresselia croagunk darmanitan deino emboar emolga "
    "exeggcute gabite girafarig gulpin haxorus"
).split()

def main():
    # get first -> last letter transitions
    nodes = set()
    arcs = defaultdict(lambda: defaultdict(list))
    for word in words:
        first = word[0]
        last = word[-1]        
        nodes.add(first)
        nodes.add(last)
        arcs[first][last].append(word)

    # create a graph
    graph = pydot.Dot("Word_combinations", graph_type="digraph")
    # use letters as nodes
    for node in sorted(nodes):
        n = pydot.Node(node, shape="circle")
        graph.add_node(n)
    # use first-last as directed edges
    for first, sub in arcs.items():
        for last, wordlist in sub.items():
            count = len(wordlist)
            label = str(count) if count > 1 else ""
            e = pydot.Edge(first, last, label=label)
            graph.add_edge(e)

    # save result
    graph.write_jpg("g:/temp/wordgraph.png", prog="dot")

if __name__=="__main__":
    main()

结果

enter image description here

这使解决方案相当明显(路径显示为红色),但仅仅因为图形是非循环的(除了两个平凡的自循环)。