简体   繁体   中英

Finding and grouping anagrams by Python

input: ['abc', 'cab', 'cafe', 'face', 'goo']
output: [['abc', 'cab'], ['cafe', 'face'], ['goo']]

The problem is simple: it groups by anagrams . The order doesn't matter.

Of course, I can do this by C++ (that's my mother tongue). But, I'm wondering this can be done in a single line by Python . EDITED: If it's not possible, maybe 2 or 3 lines. I'm a newbie in Python.

To check whether two strings are anagram, I used sorting.

>>> input = ['abc', 'cab', 'cafe', 'face', 'goo']
>>> input2 = [''.join(sorted(x)) for x in input]
>>> input2
['abc', 'abc', 'acef', 'acef', 'goo']

I think it may be doable by combining map or so. But, I need to use a dict as a hash table. I don't know yet whether this is doable in a single line. Any hints would be appreicated!

A readable one-line solution:

output = [list(group) for key,group in groupby(sorted(words,key=sorted),sorted)]

For example:

>>> words = ['abc', 'cab', 'cafe', 'goo', 'face']
>>> from itertools import groupby
>>> [list(group) for key,group in groupby(sorted(words,key=sorted),sorted)]
[['abc', 'cab'], ['cafe', 'face'], ['goo']]

The key thing here is to useitertools.groupby from the itertools module which will group items in a list together.

The list we supply to groupby has to be sorted in advanced so we pass it sorted(words,key=sorted) . The trick here is that sorted can take a key function and will sort based on the output from this function, so we pass sorted again as the key function and this will will sort the words using the letters of the string in order. There's no need to define our own function or create a lambda .

groupby takes a key function which it uses to tell if items should be grouped together and again we can just pass it the built-in sorted function.

The final thing to note is that the output is pairs of key and group objects, so we just take the grouper objects and use the list function to convert each of them to a list.

(BTW - I wouldn't call your variable input as then your hiding the built-in input function , although it's probably not one you should be using.)

the unreadable, one-line solution:

>>> import itertools
>>> input = ['abc', 'face', 'goo', 'cab', 'cafe']
>>> [list(group) for key,group in itertools.groupby(sorted(input, key=sorted), sorted)]
[['abc', 'cab'], ['cafe', 'face'], ['goo']]

(well, it is really 2 lines if you count the import...)

not a one liner but a solution...

d = {}
for item in input:
  s = "".join(sorted(item))
  if not d.has_key(s):
    d[s] = []
  d[s].append(item)
input2 = d.values()

The readable version:

from itertools import groupby
from operator import itemgetter

def norm(w):
  return "".join(sorted(w))

words = ['abc', 'cba', 'gaff', 'ffag', 'aaaa']

words_aug = sorted((norm(word), word) for word in words)

grouped = groupby(words_aug, itemgetter(0))

for _, group in grouped:
  print map(itemgetter(1), group)

The one-liner:

print list(list(anagrams for _, anagrams in group) for _, group in groupby(sorted(("".join(sorted(word)), word) for word in words), itemgetter(0)))

Prints:

[['aaaa'], ['abc', 'cba'], ['ffag', 'gaff']]

Dave's answer is concise, however the sort which is required by groupby is a O(n log(n)) operation. A faster solution is this:

from collections import defaultdict

def group_anagrams(strings):
    m = defaultdict(list)

    for s in strings:
        m[tuple(sorted(s))].append(s)

    return list(m.values())
from itertools import groupby

words = ['oog', 'abc', 'cab', 'cafe', 'face', 'goo', 'foo']

print [list(g) for k, g in groupby(sorted(words, key=sorted), sorted)]

Result:

[['abc', 'cab'], ['cafe', 'face'], ['foo'], ['oog', 'goo']]

You can't just use the groupby function, as that only groups together sequential elements for which your key function produces the same result.

The easy solution is just to sort the words first using the same function as you use for grouping.

Just to simplify the previous working answer a little, so that it's more 'readable' (extracting the sorted(words....) step out:

    >>> words = ['abc', 'eat', 'face', 'cab', 'tea', 'goo']
    >>> words = sorted(words, key=sorted)
    >>> words
    ['abc', 'cab', 'face', 'eat', 'tea', 'goo']
    >>> [list(group) for key,group in groupby(words, sorted)]
    [['abc', 'cab'], ['face'], ['eat', 'tea'], ['goo']]

As groupby expected the Sequence is in sorted order.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM