Python中使用列表的字谜

Posted

技术标签:

【中文标题】Python中使用列表的字谜【英文标题】:Anagrams in Python using lists 【发布时间】:2019-01-17 18:04:28 【问题描述】:

假设我们有以下字符串列表:

Input: ["eat", "tea", "tan", "ate", "nat", "bat"]

我们程序的输出应该对每组字谜进行分组,并将它们作为一个列表一起返回,如下所示:

Output:
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]

我当前的解决方案找到了第一组字谜,但未能检测到其他两个字谜,而是将第一组复制到列表中:

class Solution(object):
    def groupAnagrams(self, strs):
        allResults=[]
        results=[]
        temp=''
        for s in strs:  
          temp=s[1:]+s[:1]
          for i in range(0,len(strs)):
              if temp==strs[i]:
                results.append(strs[i])
          allResults.append(results)      
        return allResults 

输出是:

[["ate","eat","tea"],["ate","eat","tea"],["ate","eat","tea"],["ate","eat","tea"],["ate","eat","tea"],["ate","eat","tea"]]

如何解决这个问题?

编辑: 我通过在第二个循环之外将results 附加到allResults 中修复了附加重复:

class Solution(object):
def groupAnagrams(self, strs):
    allResults=[]
    results=[]
    temp=''
    for s in strs:  
      temp=s[1:]+s[:1]
      for i in range(0,len(strs)):
          if temp==strs[i]:
            results.append(strs[i])
    allResults.append(results) 
    print(results)
    return allResults  

但是,它没有检测到其他两组字谜。

【问题讨论】:

提示:当你进入循环的第二次迭代时,results 的值是多少?每次迭代时allResults 的值是多少? (打印出来看看。) @molbdnilo 你的意思是在if temp...之前吗? 附加到allResults后打印出来。 @molbdnilo 明白了,我知道问题是附加在错误的循环中,但由于 Python 没有 用于块,而是使用缩进,这让我感到困惑。我理解正确吗? @molbdnilo 附加中的重复已修复,但它未检测到其他两组字谜。如何解决? 【参考方案1】:

您可以使用 python 内置集合库的 defaultdict 并排序:

In [1]: l = ["eat", "tea", "tan", "ate", "nat", "bat"]

In [2]: from collections import defaultdict

In [3]: d = defaultdict(list)

In [4]: for x in l:
   ...:     d[str(sorted(x))].append(x)

In [5]: d.values()
Out[5]: dict_values([['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']])

要修复您需要的解决方案,您需要添加要检查的变量已添加,例如(同时遍历 strs 我使用 enumerate 在搜索字谜时几乎没有性能):

类解决方案(对象): def groupAnagrams(self, strs): 所有结果 = [] 添加 = set([]) 温度='' 对于 i, s in enumerate(strs): 结果 = [] unique_s = "".join(已排序) 如果添加了 unique_s: 继续 别的: added.add(unique_s) 对于 strs[i:] 中的 x: 如果 unique_s=="".join(sorted(x)): 结果.append(strs[i]) allResults.append(结果) 打印(添加) 返回所有结果

【讨论】:

很好,但是你能修改我自己的代码吗?我想知道我的代码哪里错了,以提高我对 python 的理解。 如果某些单词中有重复字母,则使用set 查找字谜将不起作用。您的代码会将“帽子”和“那个”视为字谜。 @ReblochonMasque 的主要问题是使用 set i fix,谢谢 @Ionesome 我为您的解决方案扩展了答案 好像有点problem。在我看来,您的代码更改了输入 str 并将它们作为输出返回。我想他们不应该改变。【参考方案2】:

使用itertools.groupby

>>> lst =  ["eat", "tea", "tan", "ate", "nat", "bat"]
>>> 
>>> from itertools import groupby
>>> f = lambda w: sorted(w)
>>> [list(v) for k,v in groupby(sorted(lst, key=f), f)]
[['bat'], ['eat', 'tea', 'ate'], ['tan', 'nat']]

【讨论】:

【参考方案3】:

仅使用问题标题中要求的列表:

第二行s_wordswords中每个word的所有字母,对它们进行排序,并重新创建一个由排序后的单词组成的字符串;它创建所有这些排序的字母字符串的列表,顺序与原始单词序列相同 --> 这将用于比较可能的字谜(字谜的字母在排序时产生相同的字符串)

第 3 行 indices 保存 TrueFalse 值,以表明对应的单词是否已经被提取,并避免重复。

下面的代码是一个双循环,对于每一个s_word,判断哪个其他s_word是相同的,并使用它的索引来检索原始单词列表中对应的单词;它还会更新索引的真值。

words = ["eat", "tea", "tan", "ate", "nat", "bat"]
s_words = [''.join(sorted(list(word))) for word in words]
indices = [False for _ in range(len(words))]
anagrams = []
for idx, s_word in enumerate(s_words):
    if indices[idx]:
        continue
    ana = [words[idx]]
    for jdx, word in enumerate(words):
        if idx != jdx and not indices[jdx] and s_word == s_words[jdx]:
            ana.append(words[jdx])
            indices[jdx] = True
    anagrams.append(ana)

print(anagrams)

输出:

[['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]

【讨论】:

您能解释一下您的代码吗?特别是第二行。 我添加了一些解释,结合阅读代码将帮助您了解代码的工作原理。【参考方案4】:

您实现函数的方式,您只查看字符串的旋转(即您将一个字母从开头移到结尾,例如 a-t-e -> t-e-a -> e-a-t)。如果您只切换两个字母(n-a-t -> t-a-n),您的算法无法检测到单个排列。在数学语言中,您只考虑三个字母串的偶数排列,而不考虑奇数排列。

您的代码的修改可能是:

def get_list_of_permutations(input_string):
  list_out = []
  if len(input_string) > 1:
    first_char = input_string[0]
    remaining_string = input_string[1:]
    remaining_string_permutations = get_list_of_permutations(remaining_string)
    for i in range(len(remaining_string)+1):
      for permutation in remaining_string_permutations:
        list_out.append(permutation[0:i]+first_char+permutation[i:])
  else:
    return [input_string]
  return list_out

def groupAnagrams(strs):
  allResults=[]
  for s in strs:  
    results = []
    list_of_permutations = get_list_of_permutations(s)
    for i in range(0,len(strs)):
      if strs[i] in list_of_permutations:
        results.append(strs[i])
    if results not in allResults:
      allResults.append(results)     
  return allResults 

输出是

Out[218]: [['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]

编辑:修改了代码以处理所有长度的字符串。

【讨论】:

那怎么解决呢? 你的代码的问题是它只对长度为 3 的术语有效。 你能格式化你的代码吗?缩进不合适,运行代码也不容易。 现在应该修复了。道歉。【参考方案5】:

https://docs.python.org/3/library/itertools.html#itertools.permutations

from itertools import permutations

word_list = ["eat", "tea", "tan", "ate", "nat", "bat"]
anagram_group_list = []

for word in word_list:

    if word == None:
        pass
    else:
        anagram_group_list.append([])

        for anagram in permutations(word):
            anagram = ''.join(anagram)

            try:
                idx = word_list.index(anagram)
                word_list[idx] = None 

                anagram_group_list[-1].append(anagram)

            except ValueError:
                pass # this anagram is not present in word_list

print(anagram_group_list)
# [['eat', 'ate', 'tea'], ['tan', 'nat'], ['bat']]

在重构代码并阻止它产生冗余结果后,您的代码仍然没有给出预期的结果,因为产生字谜的逻辑并不完全正确

def groupAnagrams(word_list):
    allResults=[]
    results=[]

    for idx,s in enumerate(word_list):
        if s == None:
            pass
        else:
            results = [s] # word s is added to anagram list

            # you were generating only 1 anagram like for tan --> ant but in word_list only nat was present
            for i in range(1,len(s),1):
                temp = s[i:]+s[:i] #anagram 
                    # for s = 'tan' it generates only 'ant and 'nta'
                    # when it should generate all six tna ant nta _nat_ atn tan

                if temp in word_list:
                  results.append(temp)
                  word_list[word_list.index(temp)] = None

            allResults.append(results) 

    return allResults

print(groupAnagrams(["eat", "tea", "tan", "ate", "nat", "bat"]))
# [['eat', 'ate', 'tea'], ['tan'], ['nat'], ['bat']]

【讨论】:

【参考方案6】:

可以通过集合之间的比较来检测单词的变位词,set('deltas') == set('desalt') return True

words = ["eat", "tea", "tan", "ate", "nat", "bat"]

anagrams = []

for w in words:
    m = [w2 for w2 in words if set(w2) == set(w)]
    if m not in anagrams:
        anagrams += [m]

print(anagrams)

输出

[['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]

【讨论】:

以上是关于Python中使用列表的字谜的主要内容,如果未能解决你的问题,请参考以下文章

将字符串与字符串列表进行比较以在 Python 中查找字谜

python中的字谜列表列表

Python-给定输入字母的可能的英语单字字谜

大型字谜搜索未读取到集合 Python 的末尾

字谜不匹配(字符串到列表),Python

Python故障中输入的字谜生成器