实施帕特里夏特里用作字典
-
18-09-2019 - |
题
我试图实现了Patricia Trie的与所述方法addWord()
,isWord()
和isPrefix()
作为存储大量字典中的字,以便快速检索(包括前缀搜索)的一种手段。我读过了的概念,但他们只是没有明确为实施。我想知道(在Java或Python代码)如何实现特里,特别是节点(或者我应该递归实现它)。我看到谁设置为空/无26个节点的数组实现了它一个人。有没有更好的策略(如治疗字母位),你将如何实现它?
解决方案
别人问了一个问题关于帕特里夏尝试一段时间了,我想过做一个Python实现的话,但这次我决定实际上给它一个镜头(是的,这是后落水,但它似乎是一个不错的小项目)。我取得了也许不是一个纯粹的帕特里夏·特里实现,但我喜欢我的方式更好。其他帕特里夏尝试(在其他语言)使用的仅仅是为孩子列表并检查每个孩子看到有一场比赛,但我认为这是相当低效的,所以我使用的字典。这里基本上我是如何设置它:
我将在根节点开始。根就是一本字典。这本字典有是所有单个字符导致分支(单词的第一个字母)键。与每个键对应的值是列表,其中第一项是一个字符串,它给出了与该线索的该分支相匹配的字符串的其余部分,并且第二项是导致从这个节点进一步分支的字典。此词典还具有与字的其余部分的第一个字母对应单个字符键和该过程继续向下线索。
我要提到的另一件事是,如果给定的节点有分公司,而且在特里结构本身就是一个字,然后由具有导致与列表''
一个节点字典中['',{}]
键表示。
下面是一个说明字存储一个小例子(根节点是变量_d
):
>>> x = patricia()
>>> x.addWord('abcabc')
>>> x._d
{'a': ['bcabc', {}]}
>>> x.addWord('abcdef')
>>> x._d
{'a': ['bc', {'a': ['bc', {}], 'd': ['ef', {}]}]}
>>> x.addWord('abc')
{'a': ['bc', {'a': ['bc', {}], '': ['', {}], 'd': ['ef', {}]}]}
请注意,在最后一种情况下,一个“”键被添加到词典中,以表示“ABC”是在除了一个字“ABCDEF”和“ABCABC”。
<强>源代码强>
class patricia():
def __init__(self):
self._data = {}
def addWord(self, word):
data = self._data
i = 0
while 1:
try:
node = data[word[i:i+1]]
except KeyError:
if data:
data[word[i:i+1]] = [word[i+1:],{}]
else:
if word[i:i+1] == '':
return
else:
if i != 0:
data[''] = ['',{}]
data[word[i:i+1]] = [word[i+1:],{}]
return
i += 1
if word.startswith(node[0],i):
if len(word[i:]) == len(node[0]):
if node[1]:
try:
node[1]['']
except KeyError:
data = node[1]
data[''] = ['',{}]
return
else:
i += len(node[0])
data = node[1]
else:
ii = i
j = 0
while ii != len(word) and j != len(node[0]) and \
word[ii:ii+1] == node[0][j:j+1]:
ii += 1
j += 1
tmpdata = {}
tmpdata[node[0][j:j+1]] = [node[0][j+1:],node[1]]
tmpdata[word[ii:ii+1]] = [word[ii+1:],{}]
data[word[i-1:i]] = [node[0][:j],tmpdata]
return
def isWord(self,word):
data = self._data
i = 0
while 1:
try:
node = data[word[i:i+1]]
except KeyError:
return False
i += 1
if word.startswith(node[0],i):
if len(word[i:]) == len(node[0]):
if node[1]:
try:
node[1]['']
except KeyError:
return False
return True
else:
i += len(node[0])
data = node[1]
else:
return False
def isPrefix(self,word):
data = self._data
i = 0
wordlen = len(word)
while 1:
try:
node = data[word[i:i+1]]
except KeyError:
return False
i += 1
if word.startswith(node[0][:wordlen-i],i):
if wordlen - i > len(node[0]):
i += len(node[0])
data = node[1]
else:
return True
else:
return False
def removeWord(self,word):
data = self._data
i = 0
while 1:
try:
node = data[word[i:i+1]]
except KeyError:
print "Word is not in trie."
return
i += 1
if word.startswith(node[0],i):
if len(word[i:]) == len(node[0]):
if node[1]:
try:
node[1]['']
node[1].pop('')
except KeyError:
print "Word is not in trie."
return
data.pop(word[i-1:i])
return
else:
i += len(node[0])
data = node[1]
else:
print "Word is not in trie."
return
__getitem__ = isWord
您可能已经注意到,在结束我设置__getitem__
到isWord方法。这意味着
x['abc']
将返回是否在线索与否“ABC”。
我想,也许我应该做一个模块出这个并将其提交到PyPI中,但它需要更多的测试和至少一个removeWord方法。如果您发现任何错误,让我知道,但它似乎是相当不错的了。另外,如果你看到任何效率的重大改进我也想听听他们。我已经考虑过做一些事情在每个分支的下面具有空的字典,但我要离开它了。这些空的字典可以与连接到字数据,以扩大例如执行的用途来代替。
无论如何,如果你不喜欢我实现它的方式,至少也许这会给你你想如何实现你自己的版本的一些想法。
其他提示
下面是一个使用更Python方法递归实现:
def matching_prefix_index(word1, word2):
max_len = min(len(word1),len(word2))
for i in range(max_len):
if word2[i] != word1[i]:
return i
return max_len
class PatriciaTrie(object):
def __init__(self):
self._storage = {}
self._complete_prefix_flag = False
def _find_storage_key(self, word):
for key in self._storage:
prefix_index = matching_prefix_index(key, word)
if prefix_index > 0:
return (key, prefix_index)
return (None, None)
def add(self, word):
if word == '':
self._complete_prefix_flag = True
return True
key, prefix_index = self._find_storage_key(word)
if key is not None:
if prefix_index == len(key):
return self._storage[key].add(word[len(key):])
else:
new_tree = PatriciaTrie()
new_tree._storage[key[prefix_index:]] = self._storage.pop(key)
self._storage[key[0:prefix_index]] = new_tree
return new_tree.add(word[prefix_index:])
else:
self._storage[word] = PatriciaTrie()
self._storage[word].add('')
return True
def remove(self, word):
if word == '':
self._complete_prefix_flag = False
return True
key, prefix_index = self._find_storage_key(word)
if key is None or prefix_index != len(key):
return False
subword = word[prefix_index:]
subtrie = self._storage[key]
if subtrie.remove(subword):
if (not subtrie._complete_prefix_flag) and len(subtrie._storage) == 0:
self._storage.pop(key)
return True
else:
return False
def __contains__(self, word):
if word == '':
return self._complete_prefix_flag
key, prefix_index = self._find_storage_key(word)
if key is None or prefix_index != len(key):
return False
else:
return (word[prefix_index:] in self._storage[key])
def has_prefix(self, word):
if word == '':
return True
key, prefix_index = self._find_storage_key(word)
if key is None:
return False
elif len(key) > len(word):
return (prefix_index == len(word))
elif len(key) != prefix_index:
return False
else:
return self._storage[key].has_prefix(word[prefix_index:])