繁体   English   中英

在python中用逗号运算符分隔的行中查找字谜词

[英]Finding anagrams words in a line separated with comma operator in python

>>> line = "cat,ant,ate,abc,tan,act,tea"
>>> words = line.split(",")
>>> words
['cat', 'ant', 'ate', 'abc', 'tan', 'act', 'tea']
>>> sorted_words = map(tuple, [sorted(eachword) for eachword in words])
>>> sorted_words
[('a', 'c', 't'), ('a', 'n', 't'), ('a', 'e', 't'), ('a', 'b', 'c'), ('a', 'n', 't'), ('a', 'c', 't'), ('a', 'e', 't')]
>>> repeated_words = set(sorted_words)
>>> repeated_words
set([('a', 'b', 'c'), ('a', 'e', 't'), ('a', 'c', 't'), ('a', 'n', 't')])
>>> for repeated_word in repeated_words:
    for index in [i for i, x in enumerate(sorted_words) if sorted_words.count(x) > 1 and x == repeated_word]:
        print words[index],
    print '\t'



ate tea     
cat act     
ant tan

能够获得一行中的字谜,但想知道是否有更好的方法来解决上述问题,而不是复杂性。 请帮我计算上述方法的复杂程度。

这里的大效率问题是你在每一个上做的if sorted_words.count(x) > 1

让我们来看看这些部分。 假设我们有N个元素,K个唯一元素,平均单词是长度M.

  • 对列表中的每个元素进行排序,并将结果放在另一个列表中。 这是每个元素的O(MlogM)时间,或O(NMlogM)总数。
  • 从该新列表中创建一个集合,即O(N)
  • 对于集合中的每个单词,对于列表中的每个单词,计算列表单词在列表中出现的次数。 这是个大问题。 计算列表中出现的内容的次数需要O(N)时间,并且您执行KN次数,因此这是O(N^2 * K)
  • 对于集合中的每个单词,如果count > 1 ,则迭代列表查找所有匹配的值。 这是O(NK)时间。

你可以通过提高列表理解的计数来修复O(N^2 * K)部分。 让我们假设你做到了这一点,而没有详细说明如何(这很容易)。 现在你的时间是O(NMlogM + N + NK) 假设M << K ,那就是O(NK)


要解决此问题,您需要创建从已排序单词到原始单词的映射,以便您可以在恒定时间内查找原始单词。

例如:

>>> repeated_words = {}
>>> for word in words:
...     sorted_word = tuple(sorted(word))
...     repeated_words.setdefault(sorted_word, set()).add(word)
>>> repeated_words
{('a', 'b', 'c'): {'abc'},
 ('a', 'c', 't'): {'act', 'cat'},
 ('a', 'e', 't'): {'ate', 'tea'},
 ('a', 'n', 't'): {'ant', 'tan'}}
>>> for repeated_word, words in repeated_words.viewitems():
...     if len(words) > 1:
...         print(' '.join(words))
tea ate
act cat
ant tan

现在,我们的前两个步骤是O(NMlogM + N) ,但我们的第三步是O(K)而不是O(KN) ,因为我们只是每个设置字执行一次恒定时间设置查找,而不是一个线性列表遍历每个单词。

所以我们的总时间是O(NMlogM)

(如果每组中字谜的顺序很重要,或者可能存在实际重复的单词,则可以将每个已排序的单词映射到列表而不是一组原始单词。这不会真正影响此处的性能,因为我们对列表/集合做的唯一事情是追加/添加和迭代;我只是使用了一个集合,因为从概念上看,订单是无关紧要的,不应该有任何重复。)


但我们可以做得更好。 考虑到M << K ,可能无关紧要,但......

为什么我们需要对单词进行排序? 因为如果两个单词相同,则它们的排序字母是相同的。 但是如果两个单词是相同的,那么它们的字母集也是相同的,只要没有任何重复的字母 - 在你的例子中没有。 (即使 ,你可以处理,通过使用“多重集”,如Counter ,但不变的,哈希的...虽然则比较是不太固定的时间了,他们依靠的平均数量重复的字母......让我们忽略这种复杂性,因为它与你的例子无关,但如果需要,我们可以解决它。)

>>> repeated_words = {}
>>> for word in words:
...     letter_set = frozenset(word)
...     repeated_words.setdefault(letter_set, set()).add(word)
>>> repeated_words
{frozenset({'a', 'b', 'c'}): {'abc'},
 frozenset({'a', 'e', 't'}): {'ate', 'tea'},
 frozenset({'a', 'n', 't'}): {'ant', 'tan'},
 frozenset({'a', 'c', 't'}): {'act', 'cat'}}
>>> for repeated_word, words in repeated_words.viewitems():
...     if len(words) > 1:
...         print(' '.join(words))
tea ate
ant tan
act cat

而现在,我们的总时间只是O(NM)而不是O(NMlogM)

同样,最后的改进可能不值得做(特别是如果你需要多集解决方案,因为我们花费时间来计算如何表达Counter.__eq__的复杂性,以及构建和解释FrozenCounter ,可能不仅仅是时间鉴于M << K ,我们将保存运行程序:)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM