Самая длинная неперекрывающаяся подстрока

Интересно, знает ли кто-нибудь (оптимальный?) алгоритм для самой длинной повторяющейся неперекрывающейся подстроки.

Например, в строке

АБАДЗЕДГБАДЕЗ

самым длинным повторяющимся будет «ПЛОХО». Кстати, если такого результата нет, алгоритм должен предупредить, что такое произошло. Я предполагаю, что это связано с деревьями суффиксов.

Я уверен, что это должно где-то уже существовать. Спасибо за помощь!


person Brandon Pelfrey    schedule 24.08.2009    source источник
comment
просто любопытно - какое бизнес-приложение для этого?   -  person Beth    schedule 24.08.2009
comment
Это не бизнес-приложение. Это связано с поиском темы в песне, и на данный момент это скорее любопытство, пока я не начну проект, связанный с этим. знак равно   -  person Brandon Pelfrey    schedule 24.08.2009


Ответы (4)


Поскольку вы применяете это к музыке, вы, вероятно, не ищете 100% совпадений; будут ожидаемые расхождения от одного экземпляра темы к другому. Вы можете попробовать поискать алгоритмы анализа последовательности генов — там много всего интересного. Попробуйте BLAST или другие алгоритмы выравнивания.

Вы также можете попробовать семейство алгоритмов WinEPI, а также различные реализации дерева префиксов для этого конкретного набора результатов (эти результаты допускают пропуски в подстроке; например, ABCID и AFBCD содержат ABCD). На самом деле у меня есть статья об алгоритме, на которую, возможно, стоит взглянуть, если вам это интересно; Мне нужно получить разрешение на распространение, так что дайте мне знать.

Обратите внимание, что это на самом деле ОЧЕНЬ сложная тема (чтобы сделать это эффективно) для больших наборов данных.

Источник: Исследование за год или два по сопоставимой (тематической) теме.

person Walt W    schedule 24.08.2009
comment
Если бы вы могли предоставить мне доступ, я был бы признателен! - person Brandon Pelfrey; 24.08.2009
comment
Я думаю, что он применяет это к лирике, поэтому я думаю, что он ищет совпадения на 100%. - person las3rjock; 24.08.2009
comment
@ Брэндон - я запросил разрешение, посмотрю, что я могу сделать. @las3rjock - Не совсем так. Например: Я был глупым человеком Я был глупым человеком Пример темы: Глупость в прошедшем времени. Строки не являются точным совпадением. Кроме того, во многих текстах используются разные знаки препинания и тому подобное. Так что я не уверен, что он ищет точное совпадение. - person Walt W; 24.08.2009
comment
Бах форматирование. Например, я был глупым человеком, а я был глупым, чувак. - person Walt W; 24.08.2009
comment
@Brandon - Оказывается, нет пунктов о распространении, поэтому я опубликую ссылку сегодня вечером :) - person Walt W; 24.08.2009
comment
Да, насчет лирики. Я говорю о буквальной классификации музыки по нотам. Это не имеет ничего общего с лирикой. Вы можете перекодировать музыкальные ноты в строку символов. Спасибо за проверку прав на распространение! знак равно - person Brandon Pelfrey; 25.08.2009
comment
Вот, пожалуйста - lamegame.no-ip.org/public/PrefixTrees_WWWKWDistributable.pdf Обратите внимание, что раздел алгоритмов — это раздел 4, и псевдокод там довольно явный. Не стесняйтесь задавать дополнительные вопросы о содержании или о том, как вы можете настроить его, чтобы оно работало лучше. Как и прежде, вы, вероятно, будете оперировать вхождением слова в качестве функции, а для вашего компонента времени просто используйте целые числа, обозначающие позицию в предложении. Надеюсь, это имеет смысл после того, как вы посмотрите на бумагу. - person Walt W; 25.08.2009

Массив суффиксов — хорошая структура данных для решения этой проблемы.

Решение этой проблемы есть в Programming Pearls. Джон Бентли.

person Nick Dandoulakis    schedule 24.08.2009
comment
@Nick Я не думаю, что то же самое решение в Programming Pearls можно применить здесь напрямую. Пример BANANA дает ANA, который встречается дважды и, таким образом, перекрывается, что противоречит условию, упомянутому в OP. Некоторая модификация может потребоваться для неперекрывающихся условий. Что ты говоришь? - person Abhijeet Kashnia; 11.11.2012
comment
@AbhijeetKashnia, ты прав. Возможно, мы сможем исправить это, если удостоверимся, что сравнение соседних элементов останавливается, когда смещения символов перекрываются, а не сравниваются строки целиком. - person Nick Dandoulakis; 11.11.2012

Простой алгоритм заключается в том, чтобы сделать это:

  • Создайте структуру данных, представляющую срезы строки; реализовать сравнение/сортировку в соответствии с языком
  • Создайте список каждого фрагмента строки, начиная с [первый символ, последний символ], [второй символ, последний символ], до [последний символ, последний символ]
  • Сортировать этот список - O (n log n)
  • Это объединяет все фрагменты строк с общими префиксами. Затем вы можете перебирать список, сравнивая каждую пару, чтобы увидеть, сколько символов они разделяют в начале, и если он больше вашего максимума, запишите его и установите в качестве нового максимума.

(Как показывает другой только что опубликованный ответ, я описываю здесь массив суффиксов.)

person Barry Kelly    schedule 24.08.2009
comment
Это все же довольно грубая сила. Мне интересно, есть ли более элегантный подход? Я считаю, что древовидный подход сохранит структурную информацию, а также предоставит некоторую информацию о глубине, которую можно быстро просмотреть, чтобы получить информацию о самой длинной длине. - person Brandon Pelfrey; 24.08.2009
comment
При соответствующей сортировке - см. Статью в Википедии о массиве суффиксов - время работы составляет O (n log n) в худшем случае и обычно лучше. Итерация — O(n), поэтому преобладает стоимость сортировки. Очевидная грубая сила будет как минимум O (n ^ 2) (сравнивая каждую возможную пару). Построение деревьев, вероятно, будет стоить намного больше памяти, что окажет негативное влияние на производительность в реальном мире (учитывайте кеш и т. Д.). - person Barry Kelly; 24.08.2009

Хорошо, вот одна сумасшедшая идея.

Создайте функцию, которая определяет, есть ли повторяющаяся подстрока длины k в O(n) (где n — длина текста). Это можно сделать с помощью скользящего хеша (см. Википедию для алгоритма сопоставления строк Рабина-Карпа), чтобы сгенерировать все n хэшей за линейное время и использовать хеш-таблицу/BST (или карту, или словарь, или что-то еще на вашем языке) для хранения соответствующих позиций подстрок. Прежде чем вставить текущий хэш в структуру данных, мы проверяем, не видели ли мы его раньше. Если это было замечено ранее, мы просто просматриваем индексы, где этот хэш был сгенерирован, и проверяем, является ли соответствующая подстрока неперекрывающимся совпадением.

Теперь, когда мы можем найти подстроку длины k за время O(n), мы просто запускаем двоичный поиск, чтобы найти наибольшее k, для которого мы можем найти совпадение непересекающейся подстроки, используя нашу функцию.

Код на Python следует

A=23
MOD=10007 # use a random value in the real world

def hsh(text):
    return sum([ord(c)*A**(len(text)-i-1) for i,c in enumerate(text)])%MOD

def k_substring(text, k):
    substrings={}
    cur=hsh(text[:k])
    pwr=(A**(k-1))%MOD
    substrings[cur]=[0]
    for i in xrange(k,len(text)):
        to_remove=(ord(text[i-k])*pwr)%MOD
        to_add=ord(text[i])
        cur-=to_remove
        if cur<0:
            cur+=MOD
        cur=(cur*A)%MOD
        cur=(cur+to_add)%MOD
        if cur in substrings:
            lst=substrings[cur]
            for index in lst:
                if index+k<=i-k+1 and text[index:index+k]==text[i-k+1:i+1]:
                    return index
            lst.append(i-k+1)
        else:
            substrings[cur]=[i-k+1]
    return -1

def longest_substring(text):
    hi,lo=len(text),0
    while hi>lo:
        mid=(hi+lo+1)>>1
        if k_substring(text,mid)==-1:
            hi=mid-1
        else:
            lo=mid
    index=k_substring(text,lo)
    return text[index:index+lo]

(Извините, если это неясно. Сейчас 6:30 утра, и мне действительно нужно вернуться в постель :) )

person MAK    schedule 24.08.2009