查找字符串中最重复(不是最常见)序列的算法(也称为串联重复)

时间:2018-02-19 16:26:14

标签: python parsing sequence

我正在寻找能够在字符串中找到最重复序列的算法(可能在Python中实现)。在REPETITIVE中,我的意思是在不中断的情况下反复重复的任何字符组合(串联重复)。

我正在寻找的算法&#34相同;找到最常用的词" 一个。实际上,重复块不一定是字符串中最常见的单词(子串)。

例如:

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 个答案:

答案 0 :(得分:13)

结合re.findall()(使用特定的正则表达式模式)和max()函数:

import re

#  extended sample string
s = 'asdfewfUBAUBAUBAUBAUBAasdkjnfencsADADADAD sometext'

def find_longest_rep(s):
    result = max(re.findall(r'((\w+?)\2+)', s), key=lambda t: len(t[0]))
    return result[0]

print(find_longest_rep(s))

输出:

UBAUBAUBAUBAUBA

关键模式:

  • ((\w+?)\2+)
    • (....) - 最外面捕获的组,即第一个捕获组
    • (\w+?) - 包含在第二个捕获组中的任何非空白字符序列; +? - 量词,在一次和无限次之间匹配,尽可能少,根据需要进行扩展
    • \2+ - 匹配与第二个捕获组最近匹配的文本相同的文本

答案 1 :(得分:6)

以下是基于((\w+?)\2+)正则表达式的解决方案,但还有其他改进:

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+)\2+)'), re.compile(r'((\w+?)\2+)')

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

主要特点:

  1. 使用了贪婪的((\w+)\2+)和非贪婪的((\w+)\2+?)正则表达式;
  2. 在所有子串中搜索重复子字符串,从头开始移位(例如'string'=> ['string','tring','ring','ing','ng','g']);
  3. 选择是基于不在子序列长度上的重复次数(例如'ABABABAB_ABCDEF_ABCDEF'结果将是'ABABABAB',而不是'_ABCDEF_ABCDEF');
  4. 重复序列的最小长度是重要的(参见'aaabcabc'检查)。

答案 2 :(得分:4)

您要搜索的是一种查找最大'的算法。字符串中的原始串联重复。这是一篇描述线性时间算法的论文,用于查找字符串中的所有串联重复,并通过扩展所有原始串联重复。 Gusfield. Linear Time Algorithms for Finding and Representing all Tandem Repeats in a String

答案 3 :(得分:1)

这是我写的一个强力算法。也许它会有用:

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]