Подтвердить что ты не робот

Python. В каком слове могут быть удалены самые последовательные буквы и по-прежнему быть словарем?

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

Родео, например, является хорошо известным: Родео, Роде, Род, Ро. В программе найдены "композиторы": композиторы, композитор, композит, композиты, комп.

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

Например: 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()
4b9b3361

Ответ 1

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

# 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
  • филиал
  • ранчо
  • КСД
  • ACH
  • ах
  • а

Ответ 2

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 как (Node ( "rod" ). кандидат = rodeo->rode->rod), сохраните семейство кандидатов на node И индекс письма, который вы удалили, чтобы получить каждого кандидата (Node ('rod'). Candidates = {rodeo->rod|e->rod|, road->ro|d}). Это имеет одинаковое время работы.

Ответ 3

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

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

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

# 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" при первом прохождении. В-третьих, для получения результата не нужно вычислять каждое слово или все дерево. Вместо этого он просто рекурсивно просматривает слова по мере необходимости.

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

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

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