Самая длинная общая подстрока от более двух строк - Python

StackOverflow https://stackoverflow.com/questions/2892931

Вопрос

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

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

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

Это было полезно?

Решение

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

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_SUBST как продемонстрировано 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

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

Джейсон.

Другие советы

Я предпочитаю это для 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

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

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)

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

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

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.

# 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

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

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

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

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

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!'])
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top