找到所有可能的N长度字谜 - 快速替代品

时间:2017-06-30 17:41:50

标签: python algorithm permutation anagram

我得到一系列字母,必须产生给定序列的所有N长度字谜,其中N是序列的长度。

我在python中遵循一种天真的方法,我正在采取所有的排列以实现这一目标。我发现了一些像this one这样的类似线程,但我更喜欢Python中的数学导向方法。那么对于排列来说,什么是更高效的替代方案呢?我在下面的尝试中有什么特别的错误吗?

from itertools import permutations
def find_all_anagrams(word):

pp = permutations(word)
perm_set = set()
for i in pp:
    perm_set.add(i)
ll = [list(i) for i in perm_set]
ll.sort()
print(ll)

5 个答案:

答案 0 :(得分:3)

如果有很多重复的字母,关键是只产生一次每个字谜,而不是产生所有可能的排列并消除重复。

这是一种可能只生成每个anagram一次的算法:

from collections import Counter

def perm(unplaced, prefix):
  if unplaced:
    for element in unplaced:
      yield from perm(unplaced - Counter(element), prefix + element)
  else:
    yield prefix

def permutations(iterable):
  yield from perm(Counter(iterable), "")

这实际上与产生所有排列的经典递归没有多大区别;唯一的区别是它使用collections.Counter(多重集)来保存尚未放置的元素而不是仅仅使用列表。

在迭代过程中产生的Counter个对象的数量肯定是过多的,几乎可以肯定有更快的写入方式;我选择这个版本是为了它的简单性(希望)它的清晰度

答案 1 :(得分:1)

对于具有许多相似字符的长字,这非常慢。与理论上的最大性能相比较慢。例如,permutations("mississippi")将生成比必要更长的列表。它的长度为39916800,但该组的大小为34650。

>>> len(list(permutations("mississippi")))
39916800
>>> len(set(permutations("mississippi")))
34650

所以你的方法的一大缺陷是你生成所有的字谜,然后删除重复。使用仅生成唯一字谜的方法。

编辑:

这是一些有效的,但非常丑陋且可能有错误的代码。当你读这篇文章时,我会让它变得更好。它确实为密西西比州提供了34650,所以我假设没有任何重大错误。再次警告。难看!

# Returns a dictionary with letter count
# get_letter_list("mississippi") returns 
# {'i':4, 'm':1, 'p': 2, 's':4}
def get_letter_list(word):
    w = sorted(word)
    c = 0
    dd = {}
    dd[w[0]]=1
    for l in range(1,len(w)):
        if w[l]==w[l-1]:
            d[c]=d[c]+1
            dd[w[l]]=dd[w[l]]+1
        else:
            c=c+1
            d.append(1)
            dd[w[l]]=1
    return dd

def sum_dict(d):
    s=0
    for x in d:
        s=s+d[x]
    return s

# Recursively create the anagrams. It takes a letter list
# from the above function as an argument.
def create_anagrams(dd):
    if sum_dict(dd)==1: # If there's only one letter left
        for l in dd:
            return l # Ugly hack, because I'm not used to dics
    a = []
    for l in dd:
        if dd[l] != 0:
            newdd=dict(dd)
            newdd[l]=newdd[l]-1
            if newdd[l]==0:
                newdd.pop(l)
            newl=create(newdd)
        for x in newl:
            a.append(str(l)+str(x))
    return a

>>> print (len(create_anagrams(get_letter_list("mississippi"))))
34650

它的工作方式如下:对于每个唯一的字母l,创建所有唯一的排列,少一个字母l的出现,然后将l附加到所有这些排列。

对于" mississippi",这比set(排列(单词))更快,并且它远非最佳写入。例如,字典很慢,并且在这段代码中可能需要改进很多东西,但它表明算法本身比你的方法快得多。

答案 2 :(得分:0)

也许我错过了什么,但你为什么不这样做:

from itertools import permutations

def find_all_anagrams(word):
    return sorted(set(permutations(word)))

答案 3 :(得分:0)

您可以简化为:

squishEm()

permutations的文档中,代码已经过详细说明,似乎已经过优化。

答案 4 :(得分:0)

我不知道python但是我想尝试帮助你:可能还有很多其他更高性能的算法,但我已经考虑过这个:它完全是递归的,它应该覆盖所有的情况排列。我想从一个基本的例子开始:

ABC

的排列

现在,这个算法以这种方式工作:对于Length次,你向右移动字母,但最后一个字母将成为第一个字母(你可以用队列轻松地做到这一点)。

回到例子,我们将:

  • ABC
  • BCA
  • CAB

现在重复第一个(也是唯一一个)步骤,从第二个字母到最后一个字母构建子字符串。

不幸的是,使用此算法,您无法考虑重复排列。