2013-08-05 2 views
11

Каков самый быстрый способ определить, содержит ли ключ ключ, начинающийся с конкретной строки? Можем ли мы лучше, чем линейные? Как мы можем достичь операции O (1), когда мы знаем только начало ключа?Самый быстрый способ поиска python dict с частичным ключевым словом

Вот текущее решение:

for key in dict.keys(): 
    if key.start_with(str): 
     return True 
return False 
+0

Я сомневаюсь, что вы можете что-нибудь лучше получения идеальной, как вы не можете вывести хэш ключа от части ключа. Также это оставляет место для двусмысленностей, если два ключа начинаются с того же префикса. – Hyperboreus

+0

Существуют структуры данных, которые могут это сделать, но они недоступны в стандартной библиотеке Python. Например, обрабатывает деревья или двоичные деревья поиска. – delnan

+3

Поскольку вопрос о скорости, я считаю обязанным указать, что 'для ключа в dict_:' намного быстрее, чем 'для ключа в dict_.keys():', поскольку последний строит список ключей. –

ответ

24

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

any(key.startswith(mystr) for key in mydict) 

(. Не используйте dict и str как имена переменных, те уже имена двух built-in functions)

Если вы можете предобработки dict, подумайте о том, чтобы положить ключи в дерево префикса (aka trie). Существует даже Python implementation в статье в Википедии.

+0

Три - это O (log N), а не O (1). Но это почти наверняка то, что вы хотите здесь. Это в значительной степени парадигма для структуры данных. – abarnert

+0

@abarnert Нет, если вы не сделаете странное предположение, что наибольшая длина строки логарифмична в количестве строк. Поиск в trie является линейным по длине ключа и, следовательно, не зависит от количества строк в trie. – delnan

+0

@ delnan: N - это не число строк, это число различных символов. Если у вас есть небольшое и статическое количество символов (например, с ASCII-строками), вы можете игнорировать это. Если у вас есть большое количество символов (например, произвольный Unicode), вы не можете. Либо вы в конечном итоге выполняете линейный поиск на каждом уровне trie, либо в журнале N один раз. (Да, это тоже _ линейно по длине строк, и я пренебрег этим ...) – abarnert

0

Вы можете поместить все префиксы вставленных ключей к Словарю, поэтому для ключа foo вы вставили бы f, fo и foo. Вы бы O (1) поиска, но вы бы тратить время на предварительную обработку (O (к), где к является длина ключа), и тратить много памяти:

def insert_with_prefixes(key, value, dict_): 
    prefixes = (key[:i+1] for i in xrange(len(key))) 
    dict_.update((prefix, value) for prefix in prefixes) 

Для повседневного использования я бы (и я иду) с методом в arshajii's ответ. И, конечно, иметь в виду возможных многочисленных столкновений для коротких префиксов (здесь: "h"):

>>> a = {} 
>>> insert_with_prefixes('hello', 'world', a) 
>>> insert_with_prefixes('homo', 'sapiens', a) 
>>> a 
{'h': 'sapiens', 'hom': 'sapiens', 'homo': 'sapiens', 'ho': 'sapiens', 
'hel': 'world', 'hell': 'world', 'hello': 'world', 'he': 'world'} 
Смежные вопросы