Поиск и группировка анаграмм Python

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

Проблема проста: она группируется по анаграммам. Порядок не имеет значения.

Конечно, я могу сделать это на С++ (это мой родной язык). Но мне интересно, что это можно сделать в одной строке с помощью Python. EDITED: если это невозможно, возможно, 2 или 3 строки. Я новичок в Python.

Чтобы проверить, являются ли две строки анаграммой, я использовал сортировку.

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

Я думаю, что это можно сделать, объединив map или так. Но мне нужно использовать dict как хеш-таблицу. Я еще не знаю, возможно ли это в одной строке. Любые подсказки будут полезны!

Ответ 1

Читаемое однострочное решение:

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

Например:

>>> 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']]

Ключевое значение здесь - использовать itertools.groupby из модуля itertools, который будет группировать элементы в списке вместе.

Список, который мы поставляем в groupby, должен быть отсортирован в расширенном формате, поэтому мы передаем его sorted(words,key=sorted). Фокус здесь в том, что sorted может взять ключевую функцию и будет сортироваться на основе вывода этой функции, поэтому мы снова передаем sorted в качестве ключевой функции, и это будет сортировать слова, используя буквы строки, чтобы, Нет необходимости определять нашу собственную функцию или создавать lambda.

groupby использует ключевую функцию, которую он использует, чтобы указать, должны ли элементы группироваться вместе, и снова мы можем просто передать им встроенную функцию sorted.

Последнее, что нужно отметить, это выход из пары ключевых и групповых объектов, поэтому мы просто берем объекты группы и используем функцию list для преобразования каждого из них в список.

(BTW - я бы не назвал вашу переменную input, а затем скрыл встроенную функцию input, хотя она вероятно, не тот, который вы должны использовать.)

Ответ 2

не один вкладыш, а решение...

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

Ответ 3

Читаемая версия:

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)

Однострочный:

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

Печать

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

Ответ 4

нечитаемое однострочное решение:

>>> 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']]

(ну, это действительно 2 строки, если вы считаете импорт...)

Ответ 5

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)]

Результат:

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

Вы не можете просто использовать функцию groupby, так как это объединяет только последовательные элементы, для которых ваша ключевая функция дает тот же результат.

Простое решение состоит в том, чтобы сначала отсортировать слова, используя ту же функцию, что и для группировки.

Ответ 6

Ответ на Dave является кратким, однако сортировка, требуемая groupby, - это операция O(n log(n)). Более быстрым решением является следующее:

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())