Je recherche une bibliothèque Python pour trouver la sous-chaîne commune la plus longue de n ensemble de chaînes. Il existe deux façons de résoudre ce problème:
La méthode mise en œuvre n'est pas importante. Il est important qu'il puisse être utilisé pour n ensemble de chaînes (pas seulement deux chaînes).
Ces fonctions appariées trouveront la chaîne commune la plus longue dans n'importe quel tableau arbitraire de chaînes:
def long_substr(data):
substr = ''
if len(data) > 1 and len(data[0]) > 0:
for i in range(len(data[0])):
for j in range(len(data[0])-i+1):
if j > len(substr) and is_substr(data[0][i:i+j], data):
substr = data[0][i:i+j]
return substr
def is_substr(find, data):
if len(data) < 1 and len(find) < 1:
return False
for i in range(len(data)):
if find not in data[i]:
return False
return True
print long_substr(['Oh, hello, my friend.',
'I prefer Jelly Belly beans.',
'When hell freezes over!'])
Il ne fait aucun doute que l'algorithme pourrait être amélioré et je n'ai pas eu beaucoup d'exposition à Python, alors peut-être qu'il pourrait être plus efficace syntaxiquement également, mais il devrait faire le travail.
EDIT: en ligne la deuxième fonction is_substr comme démontré par J.F. Sebastian. L'utilisation reste la même. Remarque: aucun changement d'algorithme.
def long_substr(data):
substr = ''
if len(data) > 1 and len(data[0]) > 0:
for i in range(len(data[0])):
for j in range(len(data[0])-i+1):
if j > len(substr) and all(data[0][i:i+j] in x for x in data):
substr = data[0][i:i+j]
return substr
J'espère que cela t'aides,
Jason.
Je préfère cela pour is_substr
, comme je le trouve un peu plus lisible et intuitif:
def is_substr(find, data):
"""
inputs a substring to find, returns True only
if found for each data in data list
"""
if len(find) < 1 or len(data) < 1:
return False # expected input DNE
is_found = True # and-ing to False anywhere in data will return False
for i in data:
print "Looking for substring %s in %s..." % (find, i)
is_found = is_found and find in i
return is_found
Cela peut être fait plus court:
def long_substr(data):
substrs = lambda x: {x[i:i+j] for i in range(len(x)) for j in range(len(x) - i + 1)}
s = substrs(data[0])
for val in data[1:]:
s.intersection_update(substrs(val))
return max(s, key=len)
les ensembles sont (probablement) implémentés en tant que hash-maps, ce qui rend cela un peu inefficace. Si vous (1) implémentez un type de données défini comme un trie et (2) stockez simplement les suffixes dans le trie puis forcez chaque nœud à être un point de terminaison (ce serait l'équivalent de l'ajout de toutes les sous-chaînes), ALORS en théorie, je devinerais ce bébé est assez efficace en mémoire, d'autant plus que les intersections d'essais sont super faciles.
Néanmoins, cette opération est courte et l'optimisation prématurée est à l'origine d'une perte de temps importante.
# this does not increase asymptotical complexity
# but can still waste more time than it saves. TODO: profile
def shortest_of(strings):
return min(strings, key=len)
def long_substr(strings):
substr = ""
if not strings:
return substr
reference = shortest_of(strings) #strings[0]
length = len(reference)
#find a suitable slice i:j
for i in xrange(length):
#only consider strings long at least len(substr) + 1
for j in xrange(i + len(substr) + 1, length + 1):
candidate = reference[i:j] # ↓ is the slice recalculated every time?
if all(candidate in text for text in strings):
substr = candidate
return substr
Disclaimer Cela ajoute très peu à la réponse de jtjacques. Cependant, nous espérons que cela devrait être plus lisible et plus rapide et il n'a pas ne rentre pas dans un commentaire, c'est pourquoi je poste ceci dans une réponse. Je ne suis pas satisfait de shortest_of
, pour être honnête.
def common_prefix(strings):
""" Find the longest string that is a prefix of all the strings.
"""
if not strings:
return ''
prefix = strings[0]
for s in strings:
if len(s) < len(prefix):
prefix = prefix[:len(s)]
if not prefix:
return ''
for i in range(len(prefix)):
if prefix[i] != s[i]:
prefix = prefix[:i]
break
return prefix
De http://bitbucket.org/ned/cog/src/tip/cogapp/whiteutils.py
Si quelqu'un recherche une version généralisée qui peut également prendre une liste de séquences d'objets arbitraires:
def get_longest_common_subseq(data):
substr = []
if len(data) > 1 and len(data[0]) > 0:
for i in range(len(data[0])):
for j in range(len(data[0])-i+1):
if j > len(substr) and is_subseq_of_any(data[0][i:i+j], data):
substr = data[0][i:i+j]
return substr
def is_subseq_of_any(find, data):
if len(data) < 1 and len(find) < 1:
return False
for i in range(len(data)):
if not is_subseq(find, data[i]):
return False
return True
# Will also return True if possible_subseq == seq.
def is_subseq(possible_subseq, seq):
if len(possible_subseq) > len(seq):
return False
def get_length_n_slices(n):
for i in xrange(len(seq) + 1 - n):
yield seq[i:i+n]
for slyce in get_length_n_slices(len(possible_subseq)):
if slyce == possible_subseq:
return True
return False
print get_longest_common_subseq([[1, 2, 3, 4, 5], [2, 3, 4, 5, 6]])
print get_longest_common_subseq(['Oh, hello, my friend.',
'I prefer Jelly Belly beans.',
'When hell freezes over!'])
Vous pouvez utiliser le module SuffixTree qui est un wrapper basé sur une implémentation ANSI C d'arbres de suffixes généralisés. Le module est facile à manipuler ....
Jetez un oeil à: ici