【问题标题】:Python dictionary, constant complexity way to return all keys in dict contain certain stringPython字典,返回字典中所有键的恒定复杂方式包含特定字符串
【发布时间】:2020-07-16 20:37:51
【问题描述】:

我有一本字典,例如:mydict = {'A B C':0, 'A B E':1, 'E F':0}

然后我有一个搜索键search_string = 'A B'

我想在哪里找到 search_string 是mydict.keys() 一部分的所有键和值。所以在这个可以'A B C'和'A B E'会满足。

因为 mydict 可能非常大。是否有恒定的时间复杂度来搜索这个而不是:

result = [search_string in key for key, val in mydict.items()]

如果需要,我也愿意重组字典。

【问题讨论】:

  • A B 总是在开头吗?如果需要,您可以对键进行排序和中断。
  • 是的,它们已排序。但这仍然不是常数,对吗?如果搜索是“Z”,那么我需要从头开始。这也是为了找到不完全匹配的子集,所以我想如果不经历所有事情你永远不会知道,也许这毕竟不能这样做。
  • 听起来像是一个潜在的 XY 问题。您在这里要解决的真正问题是什么?
  • 您正在从n 键(m <= n)中寻找m,您如何期望有一个 O(1)(恒定时间)解决方案?是否有任何限制,例如 search_string 始终位于键的开头/结尾?有没有提前知道的search_strings?
  • 这可能会有所帮助 - stackoverflow.com/questions/46070965/…

标签: python dictionary hashmap


【解决方案1】:

您在这里有两个潜在的解决方案 - 第一个没有 O(1) 复杂性,但它可能是您想要的方式:

我们可以尝试构建一棵树并以这种方式进行搜索 - 本质上是:

你可以让 mydict 看起来像这样:

test_dict = {
    'A': {
        'B': {
            'C': 0,
            'E': 1
        }
    },
    'E': {
        'F': 1
    }
}

def get_recursive_values(mydict):
    results = []
    for key in mydict:
        if isinstance(mydict[key], dict):
            results.extend(get_recursive_values(mydict[key]))
        else:
            results.append(mydict[key])
    return results


def search(mydict, search_text):
    components = search_text.split(' ')
    if components[0] in mydict:
        next_res = mydict[components[0]]
        if isinstance(next_res, dict):
            if len(components) == 1:
                return get_recursive_values(next_res)
            return search(next_res, " ".join(components[1:]))
        else:
            return [mydict[components[0]]]
    raise KeyError(components[0])

也许可以写得更好一点,但这对你有用 - 尝试调用search(test_dict, 'A B')

你会得到两个结果。

另一个可能的解决方案是,如果您不关心插入时间,则拥有所有不同键的所有值 - 这可能听起来有点荒谬,但您会在 O(1) 时间内获得值,但是插入时间会很大——即

   'A': [0, 1],
   'A B': [0, 1],
   'A B C': [0],
   'A B E': [1],
   'E': [1],
   'E F': [1]
}

def insert(mydict, key, value):
    for k in mydict:
        if k.startswith(key):
            mydict[k].append(value)
    mydict[key] = [value]

【讨论】:

  • 您能否详细说明第二种解决方案?我不介意插入时间。最好先准备一张搜索地图
【解决方案2】:

如果搜索始终是字符串前缀,那么您可以使用 prefix tree 或 Trie,它是现有的 Python 模块。

Trie 允许在 O(M) 时间内找到匹配项,其中 M 是最大值 字符串长度 reference (即取决于最大密钥长度而不是密钥数量)。

代码

from pytrie import StringTrie 

def create_prefix(dict):
" Creates a prefix tree based upon a dictionary "
  # create empty trie 
  trie = StringTrie() 

  for k in dict:
    trie[k] = k

  return trie

测试 1

# Preprocess to create prefix tree
mydict = {'A B C':0, 'A B E':1, 'E F':0}
prefix_tree = create_prefix(mydict)

# Now you can use search tree multile times to speed individual searches
for search_string in ['A B', 'A B C', 'E', 'B']:
  results = prefix_tree.values(search_string) # # .values resturn list that has this as a prefix
  if results:
    print(f'Search String {search_string} found in keys {results}')
  else:
    print(f'Search String {search_string} not found')

输出

Search String A B found in keys ['A B C', 'A B E']
Search String A B C found in keys ['A B C']
Search String E found in keys ['E F']
Search String B not found

测试 2(添加以回答 OP 的问题)

mydict = {'A B C':0, 'A B C D':0, 'A B C D E':0}
prefix_tree = create_prefix(mydict)
# Now you can use search tree multile times to speed individual searches
for search_string in ['A B', 'A B C', 'A B C D', 'A B C D E', 'B C']:
  results = prefix_tree.values(search_string) # # .values resturn list that has this as a prefix
  if results:
    print(f'Search String {search_string} found in keys {results}')
  else:
    print(f'Search String {search_string} not found')

输出

Search String A B found in keys ['A B C', 'A B C D', 'A B C D E']
Search String A B C found in keys ['A B C', 'A B C D', 'A B C D E']
Search String A B C D found in keys ['A B C D', 'A B C D E']
Search String A B C D E found in keys ['A B C D E']
Search String B C not found

【讨论】:

  • 这种情况是否有效:mydict = {'A B C':0, 'A B C D':0, 'A B C D E':0}?
  • @MiniMax-是的,你只需要调用prefix_tree = create_prefix(mydict)进行预处理。然后您可以执行多个搜索。假设搜索仅针对键的前缀,您将在特定字典上执行多次搜索。
  • @MiniMax--更新了测试 2 的答案,以显示正在处理您请求的附加字典。
猜你喜欢
  • 1970-01-01
  • 2014-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-12
  • 2012-11-11
  • 2015-03-10
  • 1970-01-01
相关资源
最近更新 更多