【问题标题】:The most efficient way - testing 2 strings for anagrams Python最有效的方法 - 为 anagrams Python 测试 2 个字符串
【发布时间】:2020-02-12 05:56:57
【问题描述】:

testing if strings are anagrams 有多种方式。但是,我想知道是否有一种方法可以只对每个单词进行一次迭代?如果不是,在 Python 中最有效的方法是什么?

我们可以遍历第二个字符串,检查每个字符是否存在于第一个字符串中。然而,当使用内置的 __contains__() 方法(调用__iter__() 方法)时,这给了我们在最坏的情况下(reversgram)对第一个字符串进行 n-1 次迭代。

def is_anagram(str_1, str_2):
  #chceck if same length
  if (len(str_1) != len(str_2)):
    return False
  else:
    #lowercase all characters  
    str1, str2 = list(str_1.lower()),list(str_2.lower())
    for letter in str1:
      if letter not in str2:
        return False
      str2.remove(letter)
    return True

还有其他方法吗?

【问题讨论】:

  • 为什么不对您链接的问题中的方法进行基准测试?我的钱花在了使用 Counters 的方法上

标签: python python-3.x string iteration


【解决方案1】:

我认为使用字典确实是最快的,因为排序至少需要 O(nlogn)。另一方面,创建字典应该有效地采用 O(n + n) 或 O(n)。 .get() 确保键不存在,返回默认值 0,然后添加 1 以插入键并将值初始化为字典中的 1。最后,将两个字典等同起来确保两个字典中存在相同的键:值对。或者,您可以检查两个字符串的长度,如果长度不匹配,则在开头返回 false。

def anagram_checker(str1, str2):
    str1 = str1.replace(" ", "").lower() #optional
    str2 = str2.replace(" ", "").lower() #optional

    str1_char_dict = {}
    str2_char_dict = {}

    for char in str1:
        str1_char_dict[char] = str1_char_dict.get(char, 0) + 1

    for char in str2:
        str2_char_dict[char] = str2_char_dict.get(char, 0) + 1

    return str1_char_dict == str2_char_dict

【讨论】:

    【解决方案2】:

    也许有字典?

    编辑:添加了丹的建议

    word1 = 'ahbgrettf'
    word2 = 'arethbfgt'
    
    
    def is_anagram(word1, word2):
    
        if (len(word1) != len(word2)):
            return False
    
        word_dic = {}
    
        # n iterations
        for char in word1:
            if word_dic.get(char):
                word_dic[char] += 1
            else:
                word_dic[char] = 1
    
        # n iterations
        for char in word2:
            if word_dic.get(char):
                word_dic[char] -= 1
            else:
                return False
    
        # n iterations
        for v in word_dic.values():
            if v != 0:
                return False
    
        return True
    
    
    print(is_anagram(word1, word2))
    

    总数:3n ?

    【讨论】:

    • 长度不匹配也可以提前退出
    【解决方案3】:

    您可以使用 defaultdict 来获得一个默认值并创建一个字母频率字典,然后用 O(3n) 减去另一个字符串

    from collections import defaultdict
    
    def is_anagram2(str_1, str_2):
        #check if same length
        if (len(str_1) != len(str_2)):
            return False
        #creates a dictionary with default value of 0 for all keys
        str_1_dict = defaultdict(int)
    
        #adds how many of each letter in the dictionary
        for i in str_1:
            str_1_dict[i] += 1
        #subracts how many of each letter in the dictionary
        for i in str_2:
            str_1_dict[i] -= 1
        #checks to make sure all values are 0 (same number of each letter in both strings)
        for i in str_1_dict:
            if not str_1_dict[i] == 0:
                return False
        return True
    is_anagram2('aaaa','aaaa')
    

    【讨论】:

      【解决方案4】:

      如果您可以使用 Collections.Counter,那么它就变得简单了,因为如果两个单词是字谜,它们将具有相同的键和相同的值。

      from collections import Counter
      def is_anagram(word1,word2):
          return Counter(word1)==Counter(word2)
      
      word1 = 'ahbgrettf'
      word2 = 'arethbfgt'
      
      print(is_anagram(word1,word2)
      

      如果我们使用 defaultdict,要添加到@Maxime 的答案,我们不必检查键是否存在,然后检查键是否匹配并且值是否匹配以确定它是否是字谜。

      from collections import defaultdict
      
      def is_anagram(word1,word2):
          table1, table2 = defaultdict(int), defaultdict(int)
      
          for c in word1:
              table1[c]+=1
      
          for c in word2:
              table2[c]+=1
      
          if set(table1.keys()) == set(table2.keys()):
              for k, v in table1.items():
                  if table2[k]!=v:
                      return False
          else:
              return False
          return True
      
      print(is_anagram('ahbgrettf','arethbfgt'))
      

      【讨论】:

      • Maxime 的答案使用一个字典,因此它比 defaultdict 方法在空间上更有效,不会降低计算效率。如果单词 2 有一个新字符,您也会失去提前退出。
      猜你喜欢
      • 2018-05-23
      • 1970-01-01
      • 1970-01-01
      • 2017-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多