web-dev-qa-db-ja.com

単語リストからの最長単語チェーン

したがって、これは私が作成しようとしている機能の一部です。

コードが複雑になりたくありません。

単語のリストがあります。

words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']

Wordチェーンシーケンスのアイデアは、次のWordが最後のWordが終了した文字で始まることです。

(編集:各Wordを複数回使用することはできません。それ以外の制約はありません。)

出力で最長のWordチェーンシーケンスが得られるようにします。この場合は次のとおりです。

['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']

私はそれをどうやってやるかわからない、私はこれを試す別の試みをした。それらの中の一つ...

このコードは、リストから特定のWordで始まる場合、Wordチェーンを正しく検出します。 words [0](つまり「キリン」):

words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']

Word_chain = []

Word_chain.append(words[0])

for Word in words:
    for char in Word[0]:

       if char == Word_chain[-1][-1]:
            Word_chain.append(Word)

print(Word_chain)

出力:

['giraffe', 'elephant', 'tiger', 'racoon']

しかし、私は可能な限り長い単語の連鎖を見つけたい(上記で説明)。

私の方法:ですから、リストから各Wordを開始点として使用し、各Word [0]、WordのWordチェーンを見つけて、記述してループする上記の作業コードを使用しようとしました[1]、Word [2]など。その後、ifステートメントを使用して最長のWordチェーンを検索し、その長さを以前の最長チェーンと比較しようとしましたが、適切に実行できず、実際にはわかりませんこれはどこに向かっています。

words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']

Word_chain = []
max_length = 0
for starting_Word_index in range(len(words) - 1):

    Word_chain.append(words[starting_Word_index])

    for Word in words:
        for char in Word[0]:

            if char == Word_chain[-1][-1]:
                Word_chain.append(Word)

    # Not sure

    if len(Word_chain) > max_length:
        final_Word_chain = Word_chain
        longest = len(Word_chain)
        Word_chain.clear()

print(final_Word_chain)

これは私のn回目の試みです。これは空のリストを出力します。これまでにWord_chainリストを適切にクリアできず、繰り返し単語を繰り返してしまうさまざまな試みがありました。

助けていただければ幸いです。うまくいけば、私はこれをあまりにも面倒または混乱させないでください...ありがとう!

37
Mandingo

再帰を使用して、適切な初期文字を含む可能性のあるすべての文字が実行リストに追加されたときに出現するすべての「ブランチ」を探索できます。

words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']
def get_results(_start, _current, _seen):
  if all(c in _seen for c in words if c[0] == _start[-1]):
    yield _current
  else:
      for i in words:
        if i[0] == _start[-1]:
          yield from get_results(i, _current+[i], _seen+[i])


new_d = [list(get_results(i, [i], []))[0] for i in words]
final_d = max([i for i in new_d if len(i) == len(set(i))], key=len)

出力:

['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']

このソリューションは、幅優先検索と同様に機能します。関数get_resulsは、現在の値が以前に呼び出されていない限り、リスト全体を繰り返し処理するためです。関数によって認識された値は_seenリストに追加され、最終的に再帰呼び出しのストリームを停止します。

このソリューションでは、重複した結果も無視されます。

words = ['giraffe', 'elephant', 'ant', 'ning', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse',]
new_d = [list(get_results(i, [i], []))[0] for i in words]
final_d = max([i for i in new_d if len(i) == len(set(i))], key=len)

出力:

['ant', 'tiger', 'racoon', 'ning', 'giraffe', 'elephant']
27
Ajax1234

図に示すように、新しいアイデアがあります。

enter image description here

Word [0] == Word [-1]で有向グラフを作成し、問題を変換して最大長のパスを見つけます。

16
TimeSeam

他の人が述べたように、問題は 有向非巡回グラフの最長パス を見つけることです。

Pythonに関連するグラフについては、 networkx が友達です。

グラフを初期化し、ノードを追加し、エッジを追加して、 dag_longest_path を起動するだけです。

import networkx as nx
import matplotlib.pyplot as plt

words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat',
         'hedgehog', 'mouse']

G = nx.DiGraph()
G.add_nodes_from(words)

for Word1 in words:
    for Word2 in words:
        if Word1 != Word2 and Word1[-1] == Word2[0]:
            G.add_Edge(Word1, Word2)
nx.draw_networkx(G)
plt.show()
print(nx.algorithms.dag.dag_longest_path(G))

enter image description here

以下を出力します:

['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']

注:このアルゴリズムは、グラフにサイクル(ループ)がない場合にのみ機能します。これは、無限の長さのパスがあるため、['ab', 'ba']で失敗することを意味します:['ab', 'ba', 'ab', 'ba', 'ab', 'ba', ...]

11
Eric Duminil

ブルートフォースソリューションの精神では、wordsリストのすべての順列をチェックして、最適な連続開始シーケンスを選択できます。

from itertools import permutations

def continuous_starting_sequence(words):
    chain = [words[0]]
    for i in range(1, len(words)):
        if not words[i].startswith(words[i - 1][-1]):
            break
        chain.append(words[i])
    return chain

words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']
best = max((continuous_starting_sequence(seq) for seq in permutations(words)), key=len)

print(best)
# ['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']

すべての順列を検討しているため、最大のWordチェーンから始まる順列が必要であることがわかります。

もちろん、これはO(n n!)時間の複雑さ:D

4
slider

この関数は、ジェネレーターと呼ばれるイテレーターのタイプを作成します(参照: 「yield」キーワードは何をしますか? )。同じジェネレーターのインスタンスを再帰的に作成して、可能なすべてのテールシーケンスを探索します。

words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']

def chains(words, previous_Word=None):
    # Consider an empty sequence to be valid (as a "tail" or on its own):
    yield []
    # Remove the previous Word, if any, from consideration, both here and in any subcalls:
    words = [Word for Word in words if Word != previous_Word]
    # Take each remaining Word...
    for each_Word in words:
        # ...provided it obeys the chaining rule
        if not previous_Word or each_Word.startswith(previous_Word[-1]):
            # and recurse to consider all possible tail sequences that can follow this particular Word:
            for tail in chains(words, previous_Word=each_Word):
                # Concatenate the Word we're considering with each possible tail:
                yield [each_Word] + tail  

all_legal_sequences = list(chains(words))  # convert the output (an iterator) to a list
all_legal_sequences.sort(key=len) # sort the list of chains in increasing order of chain length
for seq in all_legal_sequences: print(seq)
# The last line (and hence longest chain) prints as follows:
# ['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']

または、より効率的に最長のチェーンに直接到達するには:

print(max(chains(words), key=len)

最後に、入力で繰り返される単語を許可する代替バージョンがあります(つまり、WordをN回含めると、チェーンで最大N回使用できます)。

def chains(words, previous_Word_index=None):
    yield []
    if previous_Word_index is not None:
        previous_letter = words[previous_Word_index][-1]
        words = words[:previous_Word_index] + words[previous_Word_index + 1:]
    for i, each_Word in enumerate( words ):
        if previous_Word_index is None or each_Word.startswith(previous_letter):
            for tail in chains(words, previous_Word_index=i):
                yield [each_Word] + tail  
3
jez

この質問に対するツリーベースのアプローチがありますが、これはより高速かもしれません。私はまだコードの実装に取り​​組んでいますが、ここに私がやることがあります:

    1. Form a tree with the root node as first Word. 
    2. Form the branches if there is any Word or words that starts 
with the alphabet with which this current Word ends.
    3. Exhaust the entire given list based on the ending alphabet
 of current Word and form the entire tree.
    4. Now just find the longest path of this tree and store it.
    5. Repeat steps 1 to 4 for each of the words given in the list
 and print the longest path among the longest paths we got above.

与えられた単語のリストが大きい場合、これがより良い解決策になることを願っています。これを実際のコード実装で更新します。

2
CodeHunter

再帰的なブルートフォースアプローチが有効です。

def brute_force(pool, last=None, so_far=None):
    so_far = so_far or []
    if not pool:
        return so_far
    candidates = []
    for w in pool:
        if not last or w.startswith(last):
            c_so_far, c_pool = list(so_far) + [w], set(pool) - set([w])
            candidates.append(brute_force(c_pool, w[-1], c_so_far))
    return max(candidates, key=len, default=so_far)

>>> brute_force(words)
['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']

すべての再帰呼び出しで、これは残りのプールからすべての適格なWordでチェーンを継続しようとします。次に、そのような最長の継続を選択します。

2
schwobaseggl

再帰的アプローチを使用した別の答え:

def Word_list(w_list, remaining_list):
    max_result_len=0
    res = w_list
    for Word_index in range(len(remaining_list)):
        # if the last letter of the Word list is equal to the first letter of the Word
        if w_list[-1][-1] == remaining_list[Word_index][0]:
            # make copies of the lists to not alter it in the caller function
            w_list_copy = w_list.copy()
            remaining_list_copy = remaining_list.copy()
            # removes the used Word from the remaining list
            remaining_list_copy.pop(Word_index)
            # append the matching Word to the new Word list
            w_list_copy.append(remaining_list[Word_index])
            res_aux = Word_list(w_list_copy, remaining_list_copy)
            # Keep only the longest list
            res = res_aux if len(res_aux) > max_result_len else res 
    return res

words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']
Word_list(['dog'], words)

出力:

['dog', 'giraffe', 'elephant', 'tiger', 'racoon']
1
Pedro Torres

うまくいけば、再帰なしでそれを行うより直感的な方法。リストを反復処理し、Pythonのソートおよびリストの理解に任せてください:

words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']

def chain_longest(pivot, words):
    new_words = []
    new_words.append(pivot)
    for Word in words:
        potential_words = [i for i in words if i.startswith(pivot[-1]) and i not in new_words]
        if potential_words:
            next_Word = sorted(potential_words, key = lambda x: len)[0]
            new_words.append(next_Word)
            pivot = next_Word
        else:
            pass
    return new_words

max([chain_longest(i, words) for i in words], key = len)
>>
['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']

ピボットを設定し、ピボットWordで始まり、新しい単語リストに表示されない場合は、potential_wordsを確認します。見つかった場合は、長さで並べ替えて最初の要素を取得します。

リスト内包表記は、すべてのWordをピボットとして通過し、最長のチェーンを返します。

1
BernardL