首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >做字谜组的更好方法

做字谜组的更好方法
EN

Stack Overflow用户
提问于 2017-01-09 00:26:48
回答 2查看 705关注 0票数 2

处理下面的分组字谜问题。我目前的解决方案是按字符对每个单词进行排序,然后将相同的排序值映射到字典中。

想知道是否有更好的想法,有较少的算法时间复杂度?我正在考虑不做排序的方法,比如散列,但是散列也需要单词的顺序特征。

发布问题和我的代码,用Python2.7编写。

问题

给出一个单词列表,比如老鼠,明星,艺术,cie,冰,把相同的字谜组合成桶,然后输出它们。老鼠,明星,艺术

源代码

代码语言:javascript
复制
from collections import defaultdict
def group_anagram(anagrams):
    result = defaultdict(list)
    for a in anagrams:
        result[''.join(sorted(list(a)))].append(a)
    return result

if __name__ == "__main__":
    anagrams = ['rats', 'star', 'arts', 'cie', 'ice']
    print group_anagram(anagrams)
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-01-10 14:12:52

你现在的方法可能是最好的。为了测试一些东西,我使用了您的方法,这个方法来自@bigballer的优秀答案,第三种方法使用一个计数元组作为密钥。为了对这些方法进行压力测试,我在海量(264,097个单词)单词列表打呵欠上使用了这些方法,运行了每个函数100次,并计算了每种方法的平均时间:

代码语言:javascript
复制
from collections import defaultdict
import timeit

def group_anagram1(anagrams):
    result = defaultdict(list)
    for a in anagrams:
        result[''.join(sorted(a))].append(a)
    return result.values()

primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]

def group_anagram2(anagrams):
    result = defaultdict(list)
    for a in anagrams:
        n = 1
        for c in a:
            n *= primes[ord(c) - ord('a')]
        result[n].append(a)
    return result.values()

def group_anagram3(anagrams):
    result = defaultdict(list)
    for a in anagrams:
        counts = [0]*26
        for c in a:
            counts[ord(c) - ord('a')] += 1
        result[tuple(counts)].append(a)
    return result.values()



with open("yawl.txt") as f:
    words = f.readlines()
    words =[w.strip() for w in words]

print timeit.timeit("group_anagram1(words)", setup="from __main__ import group_anagram1,words",number = 100)/100.0
print timeit.timeit("group_anagram2(words)", setup="from __main__ import group_anagram2,words",number = 100)/100.0
print timeit.timeit("group_anagram3(words)", setup="from __main__ import group_anagram3,words",number = 100)/100.0

输出(在我的机器YMMV上):

代码语言:javascript
复制
0.486009083239
0.64333226691
0.797640375079

实际上,考虑到yawl的大小,所有的方法都非常快,每种方法处理超过25万字的时间都不到一秒。尽管如此,你最初的方法显然是赢家。此外,它并不局限于拉丁文'a''z'字母表。至于为什么它是最好的--键直接由Python内置(运行优化的C代码)构建,但其他方法使用解释的Python代码。这是很难击败内置的。

On Edit:我使用这个素数列表重新实现了第二种方法,以便将更频繁的字母(在英语中)分配给较小的素数:

代码语言:javascript
复制
primes = [5,71,37,29,2,53,59,19,11,83,79,31,43,13,7,67,97,23,17,3,41,73,47,89,61,101]

它节省了一小部分时间,但远远不足以使它比第一种方法更快。

关于进一步编辑

我重新运行上述代码,并对第二个方法进行了以下修改(如@bigballer所建议的):

代码语言:javascript
复制
primes = [5,71,37,29,2,53,59,19,11,83,79,31,43,13,7,67,97,23,17,3,41,73,47,89,61,101]
primes = {c:p for c,p in zip('abcdefghijklmnopqrstuvwxyz',primes)}

def group_anagram2(anagrams):
    result = defaultdict(list)
    for a in anagrams:
        n = 1
        for c in a:
            n *= primes[c]
        result[n].append(a)
    return result.values()

在这个版本中,前两个方法下降到一个虚拟绑定,在我有限的测试中,基于素数的方法稍微快一些(大约快8%)。尽管如此,我仍然认为第一种方法更好,因为它不依赖于固定的字母表。

票数 2
EN

Stack Overflow用户

发布于 2017-01-09 01:27:41

素数分解是唯一的,乘法的顺序并不重要。

您可以指定a = 2, b = 3, c = 5, d = 7等。

然后dab =7*2*3= 42 =3*2*7= bad,所以您的散列数为42。

另一种选择是高效地实现hash(frozenset(collections.Counter(word).items()))

编辑:最快的可能是使用26位。对于单词中的每个字符,翻转与其对应的位。您可能会遇到一些碰撞,在这种情况下,您可以进行查找。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41539431

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档