Не совсем понятно, когда вы подразумеваете под «сопоставлением» здесь, но если вы можете уменьшить это до сравнения идентификаторов, проблема сводится к поиску набора, который составляет O (1) время.
Например, если «совпадение» означает «имеет точно такой же набор символов»:
words_set = {frozenset(word) for word in words_list}
Затем, чтобы найти слово:
frozenset(word) in words_set
Или, если это означает «имеет точно такой же мультимножество символов» (т. Е. Подсчет дубликатов, но игнорирование порядка):
words_set = {sorted(word) for word in words_list}
sorted(word) in words_set
… или, если вы предпочитаете:
words_set = {collections.Counter(word) for word in words_list}
collections.Counter(word) in words_set
В любом случае, ключевая идея (без каламбура… но, возможно, так и должно было быть) заключается в том, чтобы придумать преобразование, которое превращает ваши значения (строки) в значения, которые идентичны, если они совпадают (набор символов, мультимножество символов, упорядоченный список отсортированных символов и т. д.). Тогда весь смысл набора в том, что он может искать значение, равное вашему значению в постоянное время.
Конечно, преобразование списка занимает O (N) времени (если только вы сначала не построите преобразованный набор вместо создания списка, а затем преобразуете его), но вы можете использовать его снова и снова, и это займет O (1) время каждый раз вместо O (N), что звучит так, как будто вы заботитесь о нем.
Если вам нужно вернуть соответствующее слово, а не просто знать, что оно есть, вы все равно можете сделать это с помощью набора, но это проще (если вы можете позволить себе потратить немного места) с помощью слова:
words_dict = {frozenset(word): word for word in words_list}
words_dict[frozenset(word)] # KeyError if no match
Если может быть несколько совпадений, просто измените dict на multidict:
words_dict = collections.defaultdict(set)
for word in words_list:
words_dict[frozenset(word)].add(word)
words_dict[frozenset(word)] # empty set if no match
Или, если вы явно хотите, чтобы это был список, а не набор:
words_dict = collections.defaultdict(list)
for word in words_list:
words_dict[frozenset(word)].append(word)
words_dict[frozenset(word)] # empty list if no match
Если вы хотите сделать это без использования хеш-таблиц (почему?), Вы можете использовать дерево поиска или другую логарифмическую структуру данных:
import blist # pip install blist to get it
words_dict = blist.sorteddict()
for word in words_list:
words_dict.setdefault(word, set()).add(word)
words_dict[frozenset(word)] # KeyError if no match
Это выглядит почти идентично, за исключением того факта, что не совсем тривиально оборачивать defaultdict
вокруг blist.sorteddict
- но это занимает всего несколько строк кода. (И, может быть, вам на самом деле нужен KeyError
, а не пустой набор, поэтому я решил, что стоит показать где-то как defaultdict, так и обычный dict с setdefault, так что вы можете выбрать.)
Но под прикрытием он использует гибридный вариант B-дерева вместо хеш-таблицы. Хотя это время O (log N) вместо O (1), в некоторых случаях оно на самом деле быстрее, чем dict.