更快地打印字符串中子字符串的所有起始索引,包括重叠出现

时间:2017-04-14 15:14:48

标签: python python-3.x string-matching

我正在尝试回答这个家庭作业问题:在字符串中查找所有出现的模式。不同出现的子串可以相互重叠。

  

样本1。

     

输入:

     

TACG

     

GT

     

输出:

     

说明:模式比文本长,因此文本中没有出现。

     

样本2。

     

输入:

     

ATA

     

ATATA

     

输出:

     

0 2

     

说明:图案出现在第1和第3位(这两次出现相互重叠)。

     

样本3。

     

ATAT

     

GATATATGCATATACTT

     

输出:

     

1 3 9

     

说明:图案出现在文本的第1,3和9位。

我提交的答案就是这个:

def all_indices(text, pattern):
    i = text.find(pattern)
    while i >= 0:
        print(i, end=' ')
        i = text.find(pattern, i + 1)


if __name__ == '__main__':
    text = input()
    pattern = input()
    all_indices(text, pattern)

但是,此代码未能通过最终测试用例:

  

案例#63/64失败:超出时间限制(使用时间:7.98 / 4.00,使用的内存:77647872/536870912。)

在线评委知道我用Python发送答案,并且对不同的语言有不同的时间限制。

我已经搜索了很多其他答案和方法:regexessuffix treesAho-Corasick ...但到目前为止,所有这些都落后于这个简单的解决方案(可能是因为{{ 1}}是implemented in C?)。

所以我的问题是:有没有办法更快地完成这项任务?

2 个答案:

答案 0 :(得分:1)

如果print最让您的程序变慢,那么您应该尝试尽可能少地调用它。解决问题的快速而肮脏的解决方案:

def all_indices(string, pattern):
    result = []
    idx = string.find(pattern)
    while idx >= 0:
        result.append(str(idx))
        idx = string.find(pattern, idx + 1)
    return result

if __name__ == '__main__':
    string = input()
    pattern = input()
    ' '.join(all_indices(string, pattern))

将来要正确识别代码的哪一部分会降低整个过程的速度,您可以使用python profilers

答案 1 :(得分:0)

我认为测试用例对Knuth-Morris-Pratt算法更加宽容。从https://en.wikibooks.org/wiki/Algorithm_Implementation/String_searching/Knuth-Morris-Pratt_pattern_matcher#Python复制的此代码通过了所有案例:

# Knuth-Morris-Pratt string matching
# David Eppstein, UC Irvine, 1 Mar 2002

#from http://code.activestate.com/recipes/117214/
def KnuthMorrisPratt(text, pattern):

    '''Yields all starting positions of copies of the pattern in the text.
    Calling conventions are similar to string.find, but its arguments can be
    lists or iterators, not just strings, it returns all matches, not just
    the first one, and it does not need the whole text in memory at once.
    Whenever it yields, it will have read the text exactly up to and including
    the match that caused the yield.'''

    # allow indexing into pattern and protect against change during yield
    pattern = list(pattern)

    # build table of shift amounts
    shifts = [1] * (len(pattern) + 1)
    shift = 1
    for pos in range(len(pattern)):
        while shift <= pos and pattern[pos] != pattern[pos-shift]:
            shift += shifts[pos-shift]
        shifts[pos+1] = shift

    # do the actual search
    startPos = 0
    matchLen = 0
    for c in text:
        while matchLen == len(pattern) or \
              matchLen >= 0 and pattern[matchLen] != c:
            startPos += shifts[matchLen]
            matchLen -= shifts[matchLen]
        matchLen += 1
        if matchLen == len(pattern):
            yield startPos