【问题标题】:compare 2 strings for common substring比较 2 个字符串的公共子字符串
【发布时间】:2014-06-29 16:56:11
【问题描述】:

我希望递归地找到 2 个给定字符串的最长公共子字符串。我已经编写了这段代码,但效率太低了。有没有办法我可以在 O(m*n) 中做到这一点,这里 m 和 n 分别是字符串。这是我的代码:

def lcs(x,y):
    if len(x)==0 or len(y)==0:
       return " "
    if x[0]==y[0]:
       return x[0] + lcs(x[1:],y[1:])
    t1 = lcs(x[1:],y)
    t2 = lcs(x,y[1:])
    if len(t1)>len(t2):
        return t1
    else:
        return t2
x = str(input('enter string1:'))
y = str(input('enter string2:'))
print(lcs(x,y))

【问题讨论】:

标签: python recursion substring


【解决方案1】:

你需要memoize你的递归。没有它,你最终会得到指数级的调用,因为你将一遍又一遍地重复解决同一个问题。为了使记忆查找更有效,您可以根据后缀长度而不是实际后缀来定义递归。

您还可以在 Wikipedia 上找到 DP 的 pseudocode

【讨论】:

    【解决方案2】:

    这是一个简单的非递归解决方案,它使用来自itertoolspowerset() 配方:

    from itertools import chain, combinations, product
    
    
    def powerset(iterable):
        "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
        s = list(iterable)
        return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1))
    
    
    def naive_lcs(a, b):
        return ''.join(max(set(powerset(a)) & set(powerset(b)), key=len))
    

    有问题:

    >>> naive_lcs('ab', 'ba')
    'b'
    >>> naive_lcs('ba', 'ab')
    'b'
    

    对于某些字符串对可以有多个解决方案,但我的程序会任意选择一个。

    此外,由于 任何 组合可以是最长的公共组合,并且由于计算这些组合需要 O(2 ^ n) 时间,因此该解决方案不会在 O(n * m ) 时间。通过动态编程和记忆OTOH,我们可以找到一个solution,理论上它应该表现更好:

    from functools import lru_cache
    
    
    @lru_cache()
    def _dynamic_lcs(xs, ys):
        if not (xs and ys):
            return set(['']), 0
        elif xs[-1] == ys[-1]:
            result, rlen = _dynamic_lcs(xs[:-1], ys[:-1])
            return set(each + xs[-1] for each in result), rlen + 1
        else:
            xlcs, xlen = _dynamic_lcs(xs, ys[:-1])
            ylcs, ylen = _dynamic_lcs(xs[:-1], ys)
            if xlen > ylen:
                return xlcs, xlen
            elif xlen < ylen:
                return ylcs, ylen
            else:
                return xlcs | ylcs, xlen
    
    
    def dynamic_lcs(xs, ys):
        result, _ = _dynamic_lcs(xs, ys)
        return result
    
    
    if __name__ == '__main__':
        seqs = list(powerset('abcde'))
        for a, b in product(seqs, repeat=2):
            assert naive_lcs(a, b) in dynamic_lcs(a, b)
    

    dynamic_lcs()还解决了一些pair字符串可以有多个公共最长子序列的问题。结果是这些的集合,而不是一个字符串。通过is still of exponential complexity 查找所有常见子序列的集合。

    感谢 Pradhan 提醒我动态编程和记忆。

    【讨论】:

    • 指数搜索空间不一定排除多项式时间解。实际上,一对字符串存在 O(nm) algorithm。这是递归/动态编程/记忆的经典示例 - 任你选择。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-23
    • 1970-01-01
    • 2018-04-23
    • 2023-03-29
    • 2019-11-10
    • 1970-01-01
    相关资源
    最近更新 更多