【问题标题】:Checking if a word is a subset of another word with same amount of letters检查一个单词是否是具有相同数量字母的另一个单词的子集
【发布时间】:2021-05-16 10:38:08
【问题描述】:

我正在制作一个文字游戏程序,我从文本文件中获取约 80,000 个单词的列表,然后将这些单词用作单词词典以供选择。用户请求一个特定长度的单词,然后将其加扰给他们。然后,他们猜测长度相同或更少的单词,以及使用相同字母数量或更少的单词。我有这个列表理解,以便从词典中获取所有单词,这些单词是加扰单词的子集并且也在词典中。但是,它允许出现比原始单词中更多的字母。例如:如果打乱的单词是'minute',那么'in' 应该是正确答案,但'inn' 不应该。我现在写它的方式允许这样做。这是列表理解:

correct_answers = [
    word for word in word_list
    if set(word).issubset(random_length_word)
    and word in word_list
    and len(word) <= len(random_length_word)]

所以我正在寻找类似issubset 的东西,但它只允许相同数量或更少的字母。希望这是有道理的。提前致谢。

【问题讨论】:

  • 我觉得你已经很好地理解了你想要做什么。如果我是你,我不会以列表理解的方式这样做,因为你有两个大逻辑。您可以创建一个函数来检查单个单词,然后将其应用于您拥有的所有单词。或者您可以创建一个更小的帮助函数专门用于检查频率,然后在您的列表理解中使用它。
  • 题外话,但and word in word_list是多余的

标签: python set


【解决方案1】:

我写了一个函数来玩Countdown 字母游戏。我将所需的输入称为“子集字谜”,但它可能有一个更好的技术术语。

本质上,您正在寻找的是一个multiset(来自word),它是另一个多重集(来自random_length_word)的一个子集。您可以使用collections.Counter 来完成此操作,但实际上我发现以不同的方式执行此操作要快得多:从random_length_word 中创建一个列表,然后删除word 的每个字符。由于创建新的 Counter 对象的开销,它可能更快。

def is_subset_anagram(str1, str2):
    """
    Check if str1 is a subset-anagram of str2.

    Return true if str2 contains at least as many of each char as str1.

    >>> is_subset_anagram('bottle', 'belott')  # Just enough
    True
    >>> is_subset_anagram('bottle', 'belot')  # less
    False
    >>> is_subset_anagram('bottle', 'bbeelloott')  # More
    True
    """
    list2 = list(str2)
    try:
        for char in str1:
            list2.remove(char)
    except ValueError:
        return False
    return True
>>> [w for w in ['in', 'inn', 'minute'] if is_subset_anagram(w, 'minute')]
['in', 'minute']

对于它的价值,这里是 Counter 实现:

from collections import Counter

def is_subset_anagram(str1, str2):
    delta = Counter(str1) - Counter(str2)
    return not delta

这是因为 Counter.__sub__() 产生了一个多重集,也就是说,小于 1 的计数被删除。

【讨论】:

  • 我写了一个Counter 实现(见下面我的answer),但你的方法更快,因为它不需要计算所有字符,但在大多数情况下它可以返回@987654337 @更早。 +1
  • @jonathan 啊,是的,我什至没有考虑短路!谢谢!
  • 这太好了,谢谢,我忘了在问题中提到我不允许使用任何模块,所以计数器对我来说是不可能的。谢谢!
  • @jonathan 我添加了我的Counter 实现,如果你想看看
【解决方案2】:

您的方法会丢失信息,即某个字符出现的频率,因为set(answer) 不再包含此信息。

无论如何,我认为你的方法过于复杂了。有一种更有效的方法来检查答案是否正确,而不是创建所有可能答案的列表:

我们可以只检查答案是否具有与word_list 中的任何单词匹配的字符频率。更具体地说,“匹配字符频率”意味着所有字符在答案和单词列表中的候选词中出现的频率较低(或相同)。

  1. 获取字符串的字符频率是 collections.Counter 发明的经典工作。

  2. 检查字符频率是否匹配意味着单词中的all 字符在answer 中的计数少于或相等。

  3. 最后,检查答案是否正确意味着对于any 中的word_list 中的单词,此条件为真。

from collections import Counter
from typing import List


def correct_answer(word_list: List[str], answer: str) -> bool:
    return any(
        all(  
        # this checks if each char occurs less often in the word
            Counter(answer)[character] <= Counter(word)[character]
            for character in Counter(answer).keys()
        )
        for word in word_list
    )

这比您的方法更有效,因为它占用的内存空间更少。感谢anyall 短路,也非常省时。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-02
    • 2018-05-05
    • 2012-11-02
    • 2013-06-07
    • 2019-01-06
    • 1970-01-01
    • 2015-03-28
    相关资源
    最近更新 更多