web-dev-qa-db-fra.com

Sous-chaîne commune la plus longue de plus de deux chaînes - Python

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:

  • en utilisant des arbres de suffixe
  • en utilisant la programmation dynamique.

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).

52
Nicolas NOEL

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.

55
jtjacques

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
4
kiminoa

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.

3
Herbert
# 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.

2
badp
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

2
Ned Batchelder

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!'])
1
Daniel Tripp

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

1
Rodrigue ALAHASSA