получить список анаграмм из словаря

По сути, анаграммы похожи на перестановку строк. Например, stack, sackt, stakc все являются анаграммами stack (вышеприведенные слова не имеют смысла). В любом случае, вы могли бы понять, что я в основном имел в виду.

Теперь мне нужен список из anagrams заданных миллионов слов или просто слов из словаря.

Мой основной вопрос Find total number of unique anagrams in a dictionary?

Сортировка и сравнение не будут работать, так как временная сложность довольно плохая.

Я думал об использовании хэш-таблицы, строки в качестве ключа.

Но проблема в том, какой должна быть хэш-функция? Было бы полезно, если бы был предоставлен какой-то псевдокод. Некоторые другие подходы лучше, чем упомянутые подходы, также были бы полезны.

Спасибо.


person vijay    schedule 19.06.2012    source источник
comment
вопрос не совсем ясен. не могли бы вы перефразировать цель?   -  person Nicholas DiPiazza    schedule 20.06.2012
comment
Вы имеете в виду: у меня есть словарь из миллиона слов, я хочу идентифицировать все наборы слов в словаре, которые являются анаграммами друг друга? Например. Если бы словарь содержал: [tap, pat, pot, top], вы хотели бы видеть [[tap, pat], [pot, top]]?   -  person Alex Wilson    schedule 20.06.2012
comment
да @Alex. Я просто хочу, сколько существует разных анаграмм?   -  person vijay    schedule 20.06.2012
comment
@NicholasDiPiazza, надеюсь, моя цель вам ясна.   -  person vijay    schedule 20.06.2012
comment
Решением здесь является сортировка, и ее сложность линейна, если вы принимаете некоторую постоянную верхнюю границу длины слова. Вам просто нужно правильно отсортировать; символы, а не слова.   -  person Fred Foo    schedule 20.06.2012
comment
На какой язык вы ориентируетесь?   -  person Steve Konves    schedule 20.06.2012
comment
Я, очевидно, рад, что мой ответ не принят для более приятного решения, но не могли бы вы проголосовать за него, если вы считаете, что он оказался вообще полезным? Спасибо!   -  person Alex Wilson    schedule 20.06.2012


Ответы (5)


Очевидное решение состоит в том, чтобы сопоставить каждый символ с простым числом и умножить простые числа. Итак, если «а» -> 2 и «б» -> 3, то

  • 'ab' -> 6
  • 'ba' -> 6
  • 'баб' -> 18
  • 'абба' -> 36
  • 'баба' -> 36

Чтобы свести к минимуму вероятность переполнения, наименьшие простые числа можно было присвоить более часто встречающимся буквам (e, t, i, a, n). Примечание: 26-е простое число равно 101.

ОБНОВЛЕНИЕ: здесь можно найти реализацию

person wildplasser    schedule 20.06.2012
comment
Вам все равно придется иметь дело с переполнением, которое может привести к коллизиям. Вероятно, сохраняя гистограммы частоты букв с каждой записью. - person wildplasser; 20.06.2012
comment
Ну, что ж, спасибо! Обратите внимание, что (когда вам придется иметь дело с коллизиями) он будет работать и с непростыми (случайными) числами. Это похоже на хэширование по Зобристу. Но с простыми числами это выглядит чище. - person wildplasser; 20.06.2012

Одной из возможных хэш-функций может быть (предполагая только английские слова) отсортированный подсчет количества вхождений каждой буквы. Таким образом, для «анаграммы» вы должны сгенерировать [('a', 3), ('g', 1), ('n', 1), ('m', 1), ('r', 1)].

В качестве альтернативы вы можете получить неточную группировку, сгенерировав битовую маску из вашего слова, где для битов 0-25 каждый бит представляет наличие или отсутствие этой буквы (от бита 0, представляющего «а», до бита 25, представляющего «z»). Но тогда вам придется выполнить немного больше обработки, чтобы разделить каждую хешированную группу, чтобы различать, например. «к» от «тоже».

Любая из этих идей помогает? Какой-то конкретный язык реализации (я мог бы сделать C++, python или Scala)?

Редактировать: добавлен пример кода Scala и вывод:

ОК: В данный момент я нахожусь в режиме Scala, поэтому я кое-что придумал, чтобы сделать то, что вы просите, но (кхм) это может быть не очень понятно, если вы не знакомы со Scala или функциональным программированием.

Используя большой список английских слов отсюда: http://scrapmaker.com/data/wordlists/twelve-dicts/2of12.txt

Я запускаю на них этот код Scala (занимает около 5 секунд, используя Scala 2.9 в режиме скрипта, включая время на компиляцию, со словарем около 40 000 слов. Не самый эффективный код, но первое, что пришло в голову).

// Hashing function to go from a word to a sorted list of letter counts
def toHash(b:String) = b.groupBy(x=>x).map(v => (v._1, v._2.size) ).toList.sortWith(_._1 < _._1)


// Read all words from file, one word per line
val lines = scala.io.Source.fromFile("2of12.txt").getLines

// Go from list of words to list of (hashed word, word)
val hashed = lines.map( l => (toHash(l), l) ).toList

// Group all the words by hash (hence group all anagrams together)
val grouped = hashed.groupBy( x => x._1 ).map( els => (els._1, els._2.map(_._2)) )

// Sort the resultant anagram sets so the largest come first
val sorted = grouped.toList.sortWith( _._2.size > _._2.size )

for ( set <- sorted.slice(0, 10) )
{
    println( set._2 )
}

Это выводит первые 10 наборов анаграмм (сначала наборы с наибольшим количеством членов):

List(caret, cater, crate, react, trace)
List(reins, resin, rinse, risen, siren)
List(luster, result, rustle, sutler, ulster)
List(astir, sitar, stair, stria, tarsi)
List(latrine, ratline, reliant, retinal)
List(caper, crape, pacer, recap)
List(merit, miter, remit, timer)
List(notes, onset, steno, stone)
List(lair, liar, lira, rail)
List(drawer, redraw, reward, warder)

Обратите внимание, что здесь используется первое предложение (список количества букв), а не более сложный метод битовой маски.

Редактирование 2. Вы можете заменить хеш-функцию простой сортировкой символов каждого слова (как предлагает JAB) и получить тот же результат с более четким/быстрым кодом:

def toHash(b:String) = b.toList.sortWith(_<_)
person Alex Wilson    schedule 19.06.2012
comment
Не могли бы вы помочь мне с объяснительным алгоритмом. Это было бы очень полезно. - person vijay; 20.06.2012

Если вы выполняете операцию XOR над значениями хэш-кода каждого символа, а затем выполняете операцию XOR над результатом по длине ввода, вы получите одно и то же значение независимо от порядка слов, а это означает, что все анаграммы будут давать один и тот же хэш. (Исключающее ИЛИ по длине не позволяет 'boss' и 'bo' возвращать одно и то же значение, потому что хэш 's' против самого себя всегда равен 0.)

Пример:

int AnagramHash(string input)
{
    int output = 0;

    foreach(char c in input)
        output ^= c.GetHashCode();

    return output ^ input.Length;
}

Вам все равно придется искать все слова с одним и тем же AnagramHash. Я бы обновил таблицу словаря полем для хеша (независимо от вашего алгоритма), чтобы сократить общие вычисления.

РЕДАКТИРОВАТЬ: Кроме того, в качестве примечания, XOR — это самая простая операция, выполняемая ALU, поэтому, если вы в конечном итоге используете ее, вы сможете довольно быстро генерировать свои хэши.

person Steve Konves    schedule 19.06.2012
comment
В C# GetHashCode() — это метод для всех классов. По сути, он генерирует уникальное целочисленное значение для любого объекта. (Объекты с одинаковым значением будут давать одно и то же целое число.) Для другого языка вы можете просто использовать байтовое значение каждого символа в качестве хэш-кода, потому что они все равно будут уникальными для каждого значения. - person Steve Konves; 20.06.2012
comment
Вам все равно придется искать все слова с одним и тем же AnagramHash. Нет, если вы поместите слова в списки/и т.д. которые хранятся в местах в словаре, указанном AnagramHash. - person JAB; 20.06.2012
comment
Любая проблема, если я использую простые числа для кодирования каждого из символов? - person ultimate cause; 12.05.2018

Сортировка и сравнение не будут работать, так как временная сложность довольно плохая.

Обменяв временную сложность на дополнительную память, просто сохраните количество букв в слове в массиве 26-char (или эквиваленте на любом языке, который вы используете, и предполагая, что вы используете латинский алфавит и только буквенные символы) и хешировать массив. Вы застряли со временем O(n) относительно длины слова, но большинство английских слов на самом деле не такие длинные.

например stack, sackt и stakc все будут иметь массив с местоположениями для s, t, a, c, k == 1, а все остальные будут установлены в 0.


Основываясь на вашем комментарии, из которого следует, что вы действительно в порядке с сортировкой символов слова, если вы не сортируете сами слова, вы можете сделать что-то даже более простое, чем ответ Алекса, и просто отсортировать символы в строках слов и хеше результаты. (Ларсманс сказал это первым, но не опубликовал его как ответ, так что...)

person JAB    schedule 19.06.2012
comment
По сути, меня беспокоит временная сложность. И посмотрите на другой ответ. Я думаю, что это позаботится об обеих сложностях. Спасибо. - person vijay; 20.06.2012
comment
Это так, но вы сказали, что не хотите сортировать, поэтому я дал вам кое-что, что не связано с сортировкой. - person JAB; 20.06.2012
comment
Спасибо. Извините, я где-то потерялся :P - person vijay; 20.06.2012
comment
Алекс не сортирует символы. Он делает отсортированный подсчет символов в слове, что довольно круто. В любом случае, спасибо за вашу помощь. - person vijay; 20.06.2012
comment
Однако JAB верен - сортировка символов (пока вы все еще сохраняете дубликаты) и использование этого в качестве хэша будет работать хорошо - и на самом деле, вероятно, более элегантно и эффективно, чем список от символов до счетчиков, который я предложил. - person Alex Wilson; 20.06.2012

Используйте хэш-карту со строкой в ​​качестве ключа и списком (строка) в качестве значения, где список строк содержит все анаграммы ключевой строки.

Вопрос похож на "найти все анаграммы слова в файле"

Посмотреть алгоритм и код можно здесь http://justprogrammng.blogspot.com/2012/06/determine-anagrams-of-word-in-file.html

person sachin    schedule 22.06.2012