Самая длинная общая подстрока из более чем двух строк - Python - PullRequest
51 голосов
/ 23 мая 2010

Я ищу библиотеку Python для поиска самой длинной общей подстроки из набора строк . Есть два способа решения этой проблемы:

  • с использованием суффиксных деревьев
  • с использованием динамического программирования.

Реализованный метод не важен. Важно, что он может использоваться для набора строк (не только двух строк).

Ответы [ 7 ]

53 голосов
/ 24 мая 2010

Эти парные функции найдут самую длинную общую строку в любом произвольном массиве строк:

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

Нет сомнений в том, что алгоритм можно улучшить, и у меня не было много возможностей Python, поэтому, возможно,Синтаксически он также может быть более эффективным, но он должен выполнять свою работу.

РЕДАКТИРОВАТЬ: встроил вторую функцию is_substr, как продемонстрировал JF Sebastian.Использование остается прежним.Примечание: без изменений в алгоритме.

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

Надеюсь, это поможет,

Джейсон.

4 голосов
/ 19 февраля 2013

Я предпочитаю это для is_substr, так как нахожу это немного более читабельным и интуитивно понятным:

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
3 голосов
/ 16 сентября 2015

Это можно сделать короче:

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)

множества (вероятно) реализованы в виде хэш-карт, что делает это немного неэффективным. Если вы (1) реализуете установленный тип данных в виде дерева и (2) просто сохраняете постфиксы в дереве и затем заставляете каждый узел быть конечной точкой (это было бы эквивалентно добавлению всех подстрок), то теоретически я бы предположил этот ребенок достаточно эффективен для памяти, тем более что пересечения попыток очень просты.

Тем не менее, это коротко, и преждевременная оптимизация является корнем значительного количества потерянного времени.

2 голосов
/ 24 мая 2010
# 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

Отказ от ответственности Это очень мало добавляет к ответу jtjacques. Однако, надеюсь, это должно быть более читабельным и быстрее и , что не помещалось в комментарии, поэтому я и публикую это в ответе Я не удовлетворен по поводу shortest_of, если честно.

2 голосов
/ 23 мая 2010
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

От http://bitbucket.org/ned/cog/src/tip/cogapp/whiteutils.py

1 голос
/ 05 марта 2015

Если кто-то ищет обобщенную версию, которая также может принимать список последовательностей произвольных объектов:

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 голос
/ 16 апреля 2011

Вы можете использовать модуль SuffixTree, который является оболочкой, основанной на реализации обобщенных суффиксов ANSI C. Модуль прост в обращении ....

Взгляните на: здесь

...