查找3个字符串的最长公共子序列

问题描述

找到3个等长n的字符串的最长公共子序列。

我知道仅使用2个字符串时使用的动态编程算法,该字符串的末尾是矩阵nxn,所以n ^ 2的时间和空间(如果我错了,请纠正我)。

对索引进行一些调整后,很容易对3个字符串进行相同的算法。但是时间和空间的复杂度变为n ^ 3。

我的问题是,有没有一种动态编程算法更有效?字符串长度相等的事实改变了问题中的任何内容吗?

非常感谢

解决方法

将每个字符串转换为一个Trie,以生成所有子序列。

与前两个相交,尝试为前两个共同的子序列提出一个特里。将其与第三个相交,以得出所有三个共同子序列的特里。在最后一个特里搜索最长的公共子序列。

结果为O(length_of_sequence * size_of_alphabet)。 (是的,这比朴素的动态编程lcs算法效率更高。)

这是一个证明了这一点的Python解决方案。请注意谨慎使用缓存,以免避免创建冗余数据结构。

from collections import namedtuple

LinkedList = namedtuple('LinkedList','value length tail')

class TrieNode:
    last_id = 0

    def __init__(self,char,trie=None):
        TrieNode.last_id += 1
        self.id = TrieNode.last_id
        self.char = char
        if trie is None:
            trie = {}
        self.trie = trie.copy()

def trie_from_string(string):
    trie = {}
    for i in reversed(range(len(string))):
        char = string[i]
        trie[char] = TrieNode(char,trie)
    return TrieNode('start',trie)

def intersect_tries (trie1,trie2):
    intersected = {} # Memoize to avoid repeated work
    def _intersect (t1,t2):
        key = (t1.id,t2.id)
        if key not in intersected:
            trie = {}
            for next_char,next_node in t1.trie.items():
                if next_char in t2.trie:
                    trie[next_char] = _intersect(next_node,t2.trie[next_char])
            node = TrieNode(t1.char,trie)
            intersected[key] = node
        return intersected[key]
    return _intersect(trie1,trie2)

def find_longest_in_trie (trie):
    seen = {} # Memoize to avoid repeated work.
    def _longest(t):
        if t.id not in seen:
            best = LinkedList(length=0,value=None,tail=None)
            for char,inner in t.trie.items():
                candidate = _longest(inner)
                if best.length < candidate.length:
                    best = candidate
            seen[t.id] = LinkedList(
                length = best.length + 1,value = t.char,tail = best
                )
        return seen[t.id]
    ll = _longest(trie)
    answer = []
    while ll is not None:
        answer.append(ll.value)
        ll = ll.tail

    # drop 'start' and None
    return answer[1:-1]

foo = trie_from_string('hello,world')
bar = trie_from_string('this world is weird')
baz = trie_from_string('have a wonderful day')
print(find_longest_in_trie(intersect_tries(intersect_tries(foo,bar),baz)))

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...