>>> 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
能够获得一行中的字谜,但想知道是否有更好的方法来解决上述问题,而不是复杂性。请帮我计算上述方法的复杂程度。
答案 0 :(得分:4)
这里的大效率问题是你对每一个人做的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
,可能无关紧要,但......
为什么我们需要对单词进行排序?因为如果两个单词相同,则它们的排序字母是相同的。但是如果两个单词是相同的,那么它们的字母组也相同,只要没有任何重复的字母 - 在你的例子中没有。 (即使是,你可以通过使用“multiset”来处理它,比如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__
的复杂性,以及构建和解释{{1在给定FrozenCounter
的情况下,可能超过我们保存运行程序的时间:)。