【问题标题】:Find group of strings that are anagrams查找作为字谜的字符串组
【发布时间】:2017-01-16 19:54:32
【问题描述】:

这个问题指的是this problem on lintcode。我有一个可行的解决方案,但是对于庞大的测试用例来说需要很长时间。我想知道如何改进?也许我可以减少在外循环中进行的比较次数。

class Solution:
    # @param strs: A list of strings
    # @return: A list of strings
    def anagrams(self, strs):
        # write your code here
        ret=set()
        for i in range(0,len(strs)):
            for j in range(i+1,len(strs)):
                if i in ret and j in ret:
                    continue
                if Solution.isanagram(strs[i],strs[j]):
                    ret.add(i)
                    ret.add(j)

        return [strs[i] for i in list(ret)]


    @staticmethod
    def isanagram(s, t):
        if len(s)!=len(t):
            return False
        chars={}
        for i in s:
            if i in chars:
                chars[i]+=1
            else:
                chars[i]=1

        for i in t:
            if i not in chars:
                return False
            else:
                chars[i]-=1
                if chars[i]<0:
                    return False

        for i in chars:
            if chars[i]!=0:
                return False
        return True

更新:只是添加,而不是寻找内置的pythonic解决方案,例如使用已经优化的Counter。已添加 Mike 的建议,但仍超出时限。

【问题讨论】:

  • I am wondering how can it be improved - 请查看codereview.stackexchange.com - 您的问题可能更适合我们的合作伙伴网站。请查看 CodeReview 的如何先询问,以确保它在那里受到好评。
  • 您的方法是否应该返回所有其他字符串的字谜字符串?
  • 是的,正确的,任何一对字谜。
  • isanagram 方法很复杂,看起来你正在编写 C 代码。这是 python,isanagram 只是 return Counter(word1) == Counter(word2)
  • 但是你使用了len。你已经使用了@staticmethod。你已经使用了range。您在哪里以及为什么要区分什么是“内置”和什么不是?

标签: python string anagram


【解决方案1】:

跳过您已经放置在集合中的字符串。不要再次测试它们。

# @param strs: A list of strings
# @return: A list of strings
def anagrams(self, strs):
    # write your code here
    ret=set()
    for i in range(0,len(strs)):
        for j in range(i+1,len(strs)):

            # If both anagrams exist in set, there is no need to compare them.
            if i in ret and j in ret:
                continue

            if Solution.isanagram(strs[i],strs[j]):
                ret.add(i)
                ret.add(j)

    return [strs[i] for i in list(ret)]

您还可以在遍历字母之前在字谜测试中进行长度比较。只要字符串的长度不同,它们就不能是字谜。此外,当chars 中的计数器在比较 t 中的值时达到 -1 时,只返回 false。不要再次遍历chars

@staticmethod
def isanagram(s, t):
    # Test strings are the same length
    if len(s) != len(t):
        return False

    chars={}
    for i in s:
        if i in chars:
            chars[i]+=1
        else:
            chars[i]=1

    for i in t:
        if i not in chars:
            return False
        else:
            chars[i]-=1
            # If this is below 0, return false
            if chars[i] < 0:
                return False

    for i in chars:
        if chars[i]!=0:
            return False
    return True

【讨论】:

  • 我认为您的 if 中可能有错误,因为 ret 包含索引并且您正在检查字符串本身是否在 ret 中。
  • @StephenTG 很好。谢谢!
  • 试过了,你错过了索引。但是还是超过了时限。也许isanagram可以改进?
  • @Wajahat 又想到了一个调整。但除此之外,您的解决方案看起来非常可靠。我想不出什么可以改进的地方。
  • 长度调整其实很好。不过还是超过了时限。想知道除此之外还有什么可以改进的。
【解决方案2】:

您可以创建一个字典(或collections.defaultdict),而不是比较所有字符串对,将每个字母计数映射到具有这些计数的单词。要获取字母计数,您可以使用collections.Counter。之后,您只需要从该字典中获取值。如果您想要所有单词都是任何其他单词的变位词,只需合并具有多个条目的列表即可。

strings = ["cat", "act", "rat", "hut", "tar", "tact"]
anagrams = defaultdict(list)

for s in strings:
    anagrams[frozenset(Counter(s).items())].append(s)

print([v for v in anagrams.values()])
# [['hut'], ['rat', 'tar'], ['cat', 'act'], ['tact']]
print([x for v in anagrams.values() if len(v) > 1 for x in v])
# ['cat', 'act', 'rat', 'tar']

当然,如果您不想使用内置功能,您也可以多写几行代码,也可以使用常规的 dict 而不是 defaultdict 并编写自己的 Counter,类似于您在你的isanagram 方法,只是没有比较部分。

【讨论】:

    【解决方案3】:

    您的解决方案很慢,因为您没有利用 python 的数据结构。

    这是一个在字典中收集结果的解决方案:

    class Solution:
        def anagrams(self, strs):
            d = {}
            for word in strs:
                key = tuple(sorted(word))
                try:
                    d[key].append(word)
                except KeyError:
                    d[key] = [word]
            return [w for ws in d.values() for w in ws if len(ws) > 1]
    

    【讨论】:

    • 这确实有效,但它可以在不使用排序的情况下工作吗?我认为排序也是可以接受的,因为整体时间复杂度变为O(mn logn)
    • 是的,创建任何从单词创建字典键的函数。这里唯一的要求是密钥是可散列的,key(word1) == key(word2) 当且仅当 word1 和 word2 是字谜。
    • @Wajahat 无需排序,您可以计算单词的字母,类似于您在帮助方法中所做的,并使用这些计数(以可散列格式)作为键。跨度>
    【解决方案4】:

    作为对@Mike 出色答案的补充,这是一种不错的 Pythonic 方式:

    import collections
    
    
    class Solution:
        # @param strs: A list of strings
        # @return: A list of strings
        def anagrams(self, strs):
            patterns = Solution.find_anagram_words(strs)
            return [word for word in strs if ''.join(sorted(word)) in patterns]
    
        @staticmethod
        def find_anagram_words(strs):
            anagrams = collections.Counter(''.join(sorted(word)) for word in strs)
            return {word for word, times in anagrams.items() if times > 1}
    

    【讨论】:

    • 为什么要在放入计数器之前对单词进行排序?
    • 我使用排序让不同的词看起来一样。 'lint' 和 'tiln' 看起来一样,因此它们将被视为同一个字谜。
    • 寻找算法实现而不是内置的pythonic解决方案。
    • 啊,我误会了你使用 Counter 的目的。
    • 正如@tobias_k 所说,使用sortedCounter,不要同时使用。
    【解决方案5】:

    为什么不这样?

    str1 = "cafe"
    str2 = "face"
    def isanagram(s1,s2):
        return all(sorted(list(str1)) == sorted(list(str2)))
    
    if isanagram(str1, str2):
        print "Woo"
    

    【讨论】:

    • 因为我的实现是O(n),这是O(nlogn)
    • 如果正确,return all(sorted(str1) == sorted(str2)) 会更短。你可能想要return sorted(str1) == sorted(str2)
    【解决方案6】:

    如果您在 C# 中使用 Linq,只需一行代码即可完成此操作

    字符串[] = strs; // 输入字符串数组

    var 结果 = strs.GroupBy(x => new string(x.ToCharArray().OrderBy(z => z).ToArray())).Select(g => g.ToList()).ToList( );

    【讨论】:

      【解决方案7】:

      现在要在 Python 中对 Anagrams 进行分组,我们必须: 对列表进行排序。然后,创建字典。现在字典会告诉我们这些字谜在哪里(字典索引)。那么字典的值就是字谜的实际索引。

      
      def groupAnagrams(words):
       
          # sort each word in the list
          A = [''.join(sorted(word)) for word in words]
          dict = {}
          for indexofsamewords, names in enumerate(A):
           dict.setdefault(names, []).append(indexofsamewords)
          print(dict)
          #{'AOOPR': [0, 2, 5, 11, 13], 'ABTU': [1, 3, 4], 'Sorry': [6], 'adnopr': [7], 'Sadioptu': [8, 16], ' KPaaehiklry': [9], 'Taeggllnouy': [10], 'Leov': [12], 'Paiijorty': [14, 18], 'Paaaikpr': [15], 'Saaaabhmryz': [17], ' CNaachlortttu': [19], 'Saaaaborvz': [20]}
       
          for index in dict.values():
           print([words[i] for i in index])
       
      
      if __name__ == '__main__':
       
          # list of words
          words = ["ROOPA","TABU","OOPAR","BUTA","BUAT" , "PAROO","Soudipta",
              "Kheyali Park", "Tollygaunge", "AROOP","Love","AOORP", "Protijayi","Paikpara","dipSouta","Shyambazaar",
              "jayiProti", "North Calcutta", "Sovabazaar"]
       
          groupAnagrams(words)
      

      输出:

      
      ['ROOPA', 'OOPAR', 'PAROO', 'AROOP', 'AOORP']
      ['TABU', 'BUTA', 'BUAT']
      ['Soudipta', 'dipSouta']
      ['Kheyali Park']
      ['Tollygaunge']
      ['Love']
      ['Protijayi', 'jayiProti']
      ['Paikpara']
      ['Shyambazaar']
      ['North Calcutta']
      ['Sovabazaar']
      

      【讨论】:

        猜你喜欢
        • 2012-02-16
        • 2014-04-13
        • 2021-11-19
        • 2021-09-16
        • 2011-12-12
        • 2015-12-27
        • 2014-10-24
        • 1970-01-01
        • 2020-11-08
        相关资源
        最近更新 更多