Python - Какое слово может удалить большинство последовательных букв и все же быть словарным словом? - PullRequest
12 голосов
/ 22 мая 2011

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

Родео, например, хорошо известно: Родео, РодеРод, Ро.Программа нашла «композиторов»: Композиторы, Композитор, Композит, Композит, Комп

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

Например: beast, best, bet, be - будет допустимой возможностью

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

#Recursive function that finds how many letters can be removed from a word and
#it still be valid.  
def wordCheck(word, wordList, counter):

    if len(word)>=1:
        if word in wordList:
            return (wordCheck(word[0:counter-1], wordList, counter-1))
        else:
            return counter
    return counter


def main():
    a = open('C:\\Python32\\megalist2.txt', 'r+')
    wordList = set([line.strip() for line in a])
    #megaList contains a sorted list of tuple of 
    #(the word, how many letters can be removed  consecutively)
    megaList = sorted([(i, len(i)-1- wordCheck(i, wordList, len(i))) for i in wordList], key= lambda megaList: megaList[1])


    for i in megaList:
        if i[1] > 3:
            print (i)

if __name__ == '__main__':
    main()

Ответы [ 3 ]

10 голосов
/ 22 мая 2011
for each english word W:
    for each letter you can remove:
        remove the letter
        if the result W' is also word:
            draw a line W->W'
for each english word W:
    connect ROOT-> each english word W
use a graph search algorithm to find the longest path starting at ROOT
    (specifically, the words are now in a directed acyclic graph; traverse
    the graph left-right-top-down, i.e. in a "topological sort", keeping
    track of the longest candidate path to reach each node; this achieves 
    the result in linear time)

этот алгоритм занимает только линейное O (# wordsInEnglish * averageWordLength) время!в основном, столько времени, сколько нужно, чтобы прочитать входные данные

. Его можно легко изменить, чтобы найти последовательных удаленных букв: вместо сохранения одного кандидата на узел, подобного (Node ('rod').кандидат = rodeo->rode->rod), сохраняйте семейство кандидатов на узел И индекс буквы, которую вы удалили, чтобы получить каждого кандидата (Узел ('стержень'). кандидаты = {rodeo->rod|e->rod|, road->ro|d}).Это имеет то же время выполнения.

8 голосов
/ 22 мая 2011

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

# Load the words into a dictionary
words = dict((x.strip(), set()) for x in open("/usr/share/dict/words"))

# For each word, remove each letter and see if the remaining word is still
# in the dictionary. If so, add it to the set of shorter words associated with
# that word in the dictionary.
# For example, bear -> {ear, bar, ber}
for w in words:
    for i in range(len(w)):
        shorter = w[:i] + w[i+1:]
        if shorter in words:
            words[w].add(shorter)

# Sort the words by length so we process the shortest ones first
sortedwords = sorted(words, key=len)

# For each word, the maximum chain length is:
#  - the maximum of the chain lengths of each shorter word, if any
#  - or 0 if there are no shorter words for this word
# Note that because sortedwords is sorted by length, we will always
# have maxlength[x] already available for each shorter word x
maxlength = {}
for w in sortedwords:
    if words[w]:
        maxlength[w] = 1 + max(maxlength[x] for x in words[w])
    else:
        maxlength[w] = 0

# Print the words in all chains for each of the top 10 words
toshow = sorted(words, key=lambda x: maxlength[x], reverse=True)[:10]
while toshow:
    w = toshow[0]
    print(w, [(x, maxlength[x]) for x in words[w]])
    toshow = toshow[1:] + list(x for x in words[w] if x not in toshow)

Самая длинная цепочка слов в моем словаре:

  • безжаберный
  • жабровидный
  • жабры
  • жабры
  • Branchi
  • филиал
  • 1019 * ранчо *
  • КСД
  • ACH
  • ах
  • а
1 голос
/ 22 мая 2011

Может быть, я просто упускаю суть упражнения, но разве простое эвристическое правило не должно урезать большую часть поиска?Особенно, если вы пытаетесь просто найти одно слово, которое может вырезать наибольшее количество букв, вам, вероятно, просто захочется взглянуть на самые большие слова и проверить, не содержат ли они одно из самых маленьких.

Например,огромное количество слов включает буквы «а» и «я», которые являются действительными английскими словами.Более того, более длинные слова будут все чаще иметь одну или обе буквы.Вероятно, вы можете пропустить любое слово, которое не имеет «a» или «i» сразу.

Возможно, вы могли бы использовать это в решении Грега, на самом деле, если вы сначала получите свою отсортированную копию списка слов,то есть:

# Similar to Greg's.  Reads into a dict
words = dict((x.strip(), None) for x in open("/usr/share/dict/words"))
# Generates a reverse sorted list from the dict (largest words first)
sortedwords = sorted(words, key=len, reverse=True)

# Largest possible reduction is making a longest word into 1 letter
longestPossible = len(sortedWords[0])-1

# Function that recursively finds shorter words and keeps count of reductions
def getMaxLettersRemoved(w, words, alreadyRemovedCount=0):
    # If you've already calculated the value, return it
    if words[w] is not None:
        return words[w]
    # Recursively calculate how many letters you can remove
    shorterPossibilities = [w[:i] + w[i+1:] for i in xrange(len(w))]
    # Calculate how max # of letters you can remove from shortened words
    totalRemoved = max([getMaxLettersRemoved(w, words, alreadyRemovedCount+1) for shorter in shorterPossibilities if shorter in words])
    # Total removed will be the same or will increase due to removals from shorter words
    totalRemoved = max(totalRemoved, alreadyRemovedCount)
    # Cache the result and return it
    words[w] = totalRemoved
    return totalRemoved 

# Go through words from largest to smallest, so long as they have 'a' or 'i'
bestNumRemoved = 0
for w in sortedwords:
    if 'a' in w or 'i' in w:
        # Get the number of letters you can remove
        numRemoved = getMaxLettersRemoved(w, words)
        # Save the best one found
        if numRemoved > bestNumRemoved:
            bestWord = w
            bestNumRemoved = numRemoved 
        # Stop if you can't do better
        if bestNumRemoved >= len(w)-1:
            break

# Need to make sure the best one found is better than any left
if bestNumRemoved < longestPossible:
    for w in sortedwords:
        # Get the number of letters you can remove
        numRemoved = getMaxLettersRemoved(w, words)
        # Save the best one found
        if numRemoved > bestNumRemoved:
            bestWord = w
            bestNumRemoved = numRemoved 
        # Stop if you can't do better
        if bestNumRemoved >= len(w)-2:
            break

Так что это несколько отличается.Во-первых, он сортируется первым, чтобы вы могли получить самые большие слова.Во-вторых, он полностью игнорирует любое слово, не содержащее «a» или «i» при первом проходе.В-третьих, для получения результата не нужно вычислять каждое слово или все дерево.Вместо этого он просто рекурсивно просматривает слова по мере необходимости.

Каждый раз, когда он вырезает букву и находит новое слово, он запускает ту же функцию, чтобы узнать количество букв, которые он может вырезать из этого меньшего слова, плюс число, уже удаленное из любого корневого слова, которое онопришли из.Теоретически это должно быть достаточно быстрым, поскольку не нужно выполнять большинство слов, поскольку он выполняет типичный прием оптимизации: проверяет, находится ли он на оптимальной границе.Во-первых, он находит наилучшую возможность среди тех, у кого есть «я» или «а».Затем он проверяет слова длиннее найденного лучшего, чтобы убедиться, что нет лучшего варианта, который не содержит ни одной буквы, но длиннее как минимум на 2 буквы (так что теоретически это может быть лучше).

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

Кроме того, я не совсем убежден, что сортировка спискаключи стоит того.Хотя алгоритм сортировки python работает довольно быстро, он все еще имеет дело с большим списком и может иметь довольно значительные затраты.Идеальный алгоритм, вероятно, должен был бы принять во внимание эту стоимость и решить, стоит ли это того или нет (вероятно, нет).Если вы не сортируете список, вы, вероятно, захотите, чтобы на первом проходе рассматривались только слова определенной минимальной длины, возможно, даже как часть большого цикла.В действительности нет никакого смысла вычислять что-либо, имеющее отношение к словам, которые могут не иметь ничего общего с решением.

...