Алгоритм поиска наиболее повторяющейся (не самой распространенной) последовательности в строке (он же тандем повторяется)
Я ищу алгоритм (возможно, реализованный в Python), способный найти самую повторяющуюся последовательность в строке. Где для повторяющихся, я имею в виду любую комбинацию символов, которая повторяется снова и снова без перерыва (тандемный повтор).
алгоритм, который я ищу не же "найти наиболее распространенное слово" один. Фактически, повторяющийся блок не должен быть самым распространенным словом (подстрокой) в строка.
например:
s = 'asdfewfUBAUBAUBAUBAUBAasdkBAjnfBAenBAcs'
> f(s)
'UBAUBAUBAUBAUBA' #the "most common word" algo would return 'BA'
к сожалению, я понятия не имею, как справиться с этим. Любая помощь очень приветствуется.
обновление
немного дополнительный пример, чтобы уточнить, что я хочу вернуть последовательность с наибольшим количеством повторений, независимо от ее основного строительного блока.
g = 'some noisy spacer'
s = g + 'AB'*5 + g + '_ABCDEF'*2 + g + 'AB'*3
> f(s)
'ABABABABAB' #the one with the most repetitions, not the max len
примеры из @rici:
s = 'aaabcabc'
> f(s)
'abcabc'
s = 'ababcababc'
> f(s)
'ababcababc' #'abab' would also be a solution here
# since it is repeated 2 times in a row as 'ababcababc'.
# The proper algorithm would return both solutions.
4 ответов
вот решение, основанное на ((\w+?)+)
регулярное выражение, но с дополнительными улучшениями:
import re
from itertools import chain
def repetitive(sequence, rep_min_len=1):
"""Find the most repetitive sequence in a string.
:param str sequence: string for search
:param int rep_min_len: minimal length of repetitive substring
:return the most repetitive substring or None
"""
greedy, non_greedy = re.compile(r'((\w+)+)'), re.compile(r'((\w+?)+)')
all_rep_seach = lambda regex: \
(regex.search(sequence[shift:]) for shift in range(len(sequence)))
searched = list(
res.groups()
for res in chain(all_rep_seach(greedy), all_rep_seach(non_greedy))
if res)
if not sequence:
return None
cmp_key = lambda res: res[0].count(res[1]) if len(res[1]) >= rep_min_len else 0
return max(searched, key=cmp_key)[0]
вы можете проверить это вот так:
def check(seq, expected, rep_min_len=1):
result = repetitive(seq, rep_min_len)
print('%s => %s' % (seq, result))
assert result == expected, expected
check('asdfewfUBAUBAUBAUBAUBAasdkBAjnfBAenBAcs', 'UBAUBAUBAUBAUBA')
check('some noisy spacerABABABABABsome noisy spacer_ABCDEF_ABCDEFsome noisy spacerABABAB', 'ABABABABAB')
check('aaabcabc', 'aaa')
check('aaabcabc', 'abcabc', rep_min_len=2)
check('ababcababc', 'ababcababc')
check('ababcababcababc', 'ababcababcababc')
основные характеристики:
- используется жадный
((\w+)+)
и нежадный((\w+)+?)
регулярное выражение; - поиск повторяющейся подстроки во всех подстроках со сдвигом от начала (например, 'String' = > ['string', 'tring', 'ring',' ing',' ng','g']);
- выбор основан на количестве повторений не на длина подпоследовательности (например, для результата "ABABABAB_ABCDEF_ABCDEF" будет "ABABABAB", а не "_ABCDEF_ABCDEF");
- минимальная длина повторяющейся последовательности имеет значение (см. проверку "aaabcabc").
С re.findall()
(используя specific выражение Паттен) и max()
функции:
import re
# extended sample string
s = 'asdfewfUBAUBAUBAUBAUBAasdkjnfencsADADADAD sometext'
def find_longest_rep(s):
result = max(re.findall(r'((\w+?)+)', s), key=lambda t: len(t[0]))
return result[0]
print(find_longest_rep(s))
вывод:
UBAUBAUBAUBAUBA
критический шаблон:
-
((\w+?)+)
:-
(....)
- самая внешняя захваченная группа, которая является 1-й захваченной группой -
(\w+?)
- любая последовательность символов без пробелов, заключенная во 2-ю захваченную группу;+?
- Квантор, матчи между одним и неограниченное время, как можно меньше раз, расширяясь по мере необходимости -
+
- соответствует тому же тексту, что и в последнее время соответствует 2-й группе захвата
-
то, что вы ищете, - это алгоритм поиска "самого большого" примитивного тандемного повтора в строке. Вот статья, описывающая алгоритм линейного времени, чтобы найти все тандемные повторы в строке и, следовательно, все примитивные тандемные повторы. Gusfield. Алгоритмы линейного времени для поиска и представления всех тандемных повторов в строке
вот алгоритм грубой силы, который я написал. Может быть, это будет полезно:
def find_most_repetitive_substring(string):
max_counter = 1
position, substring_length, times = 0, 0, 0
for i in range(len(string)):
for j in range(len(string) - i):
counter = 1
if j == 0:
continue
while True:
if string[i + counter * j: i + (counter + 1) * j] != string[i: i + j] or i + (counter + 1) * j > len(string):
if counter > max_counter:
max_counter = counter
position, substring_length, times = i, j, counter
break
else:
counter += 1
return string[position: position + substring_length * times]