Beginnen wir mit dem Baum
from collections import defaultdict
def identity(x):
return x
class TreeReprMixin(object):
def __repr__(self):
base = dict(self)
return repr(base)
class PrefixTree(TreeReprMixin, defaultdict):
'''
A hash-based Prefix or Suffix Tree for testing for
sequence inclusion. This implementation works for any
slice-able sequence of hashable objects, not just strings.
'''
def __init__(self):
defaultdict.__init__(self, PrefixTree)
self.labels = set()
def add(self, sequence, label=None):
layer = self
if label is None:
label = sequence
if label:
layer.labels.add(label)
for i in range(len(sequence)):
layer = layer[sequence[i]]
if label:
layer.labels.add(label)
return self
def add_ngram(self, sequence, label=None):
if label is None:
label = sequence
for i in range(1, len(sequence) + 1):
self.add(sequence[:i], label)
def __contains__(self, sequence):
layer = self
j = 0
for i in sequence:
j += 1
if not dict.__contains__(layer, i):
break
layer = layer[i]
return len(sequence) == j
def depth_in(self, sequence):
layer = self
count = 0
for i in sequence:
if not dict.__contains__(layer, i):
print "Breaking"
break
else:
layer = layer[i]
count += 1
return count
def subsequences_of(self, sequence):
layer = self
for i in sequence:
layer = layer[i]
return layer.labels
def __iter__(self):
return iter(self.labels)
class SuffixTree(PrefixTree):
'''
A hash-based Prefix or Suffix Tree for testing for
sequence inclusion. This implementation works for any
slice-able sequence of hashable objects, not just strings.
'''
def __init__(self):
defaultdict.__init__(self, SuffixTree)
self.labels = set()
def add_ngram(self, sequence, label=None):
if label is None:
label = sequence
for i in range(len(sequence)):
self.add(sequence[i:], label=label)
den Baum zu bevölkern, dann würden Sie die .add_ngram
Methode verwenden.
Der nächste Teil ist ein wenig komplizierter, da sie eine gleichzeitige Traversal von Strings, während der Verfolgung des Baum Koordinaten suchen. Ziehen alle diese weg, müssen wir einige Funktionen, die auf dem Baum arbeiten und eine Abfrage-String
def overlapping_substrings(string, tree, solved=None):
if solved is None:
solved = PrefixTree()
i = 1
last = 0
matching = True
solutions = []
while i < len(string) + 1:
if string[last:i] in tree:
if not matching:
matching = True
else:
i += 1
continue
else:
if matching:
matching = False
solutions.append(string[last:i - 1])
last = i - 1
i -= 1
i += 1
if matching:
solutions.append(string[last:i])
for solution in solutions:
if solution in solved:
continue
else:
solved.add_ngram(solution)
yield solution
def slide_start(string):
for i in range(len(string)):
yield string[i:]
def seek_subtree(tree, sequence):
# Find the node of the search tree which
# is found by this sequence of items
node = tree
for i in sequence:
if i in node:
node = node[i]
else:
raise KeyError(i)
return node
def find_all_common_spans(string, tree):
# We can keep track of solutions to avoid duplicates
# and incomplete prefixes using a Prefix Tree
seen = PrefixTree()
for substring in slide_start(string):
# Drive generator forward
list(overlapping_substrings(substring, tree, seen))
# Some substrings are suffixes of other substrings which you do not
# want
compress = SuffixTree()
for solution in sorted(seen.labels, key=len, reverse=True):
# A substrings may be a suffix of another substrings, but that substrings
# is actually a repeating pattern. If a solution is
# a repeating pattern, `not solution in seek_subtree(tree, solution)` will tell us.
# Otherwise, discard the solution
if solution in compress and not solution in seek_subtree(tree, solution):
continue
else:
compress.add_ngram(solution)
return compress.labels
def search(query, corpus):
tree = SuffixTree()
if isinstance(corpus, SuffixTree):
tree = corpus
else:
for elem in corpus:
tree.add_ngram(elem)
return list(find_all_common_spans(query, tree))
So, jetzt das, was zu tun Sie wollten, dies tun:
search("12345", ["51234"])
search("623456", ["12345623456"])
Wenn etwas unklar ist, Bitte lassen Sie es mich wissen, und ich werde versuchen zu klären.
Es scheint, dass es anhängt Off-by-one-Lösungen: Suche ('abc', 'b') >> [ 'bc'] auch versuchen, herauszufinden, warum es erste kürzeste Lösungen findet: Suche ('test', 'Tests') >> [ 'te', 'st'] –
Es ist wahrscheinlich, dass ein off-by-one-Fehler in 'overlapping_substrings' seit Index-Management immer noch ein bisschen slap-dash war. Bitte beachten Sie auch, dass in meinem Anwendungsbeispiel der "Korpus" eine Liste von Strings ist, nicht eine einzelne Zeichenkette. – mobiusklein
Danke.Es war tatsächlich in der __contains__ Prozedur, j = j + 1 Inkremente, bevor die Prüfung durchgeführt wird, also ist "bc" im n_gram ' b'. Auch gefunden, dass ich keine n_grams bereits hinzugefügt hinzufügen wollte, da dies Doppelprüfung/seltsame Ergebnisse vermeiden wird. –