Trie和子序列

7
我们有两个集合A和B。每个集合都包含字符串。 例如:A - {"abwcd", "dwas", "www"} 和 B - {"opqr", "tops", "ibmd"} 我该如何计算在集合A的所有字符串中出现,但在集合B的任何字符串中都没有出现的子序列数量?对于上面的例子,答案是1(即子序列"w")。
要以最优的方式完成这一任务。我考虑使用两个Trie树,第一次将B集合中所有字符串的所有子序列放入Trie t_B中,然后,我开始将A集合中所有字符串的所有子序列放入Trie t_A中,如果在同一字符串中之前找到了相同的子序列(例如:如果我有字符串“aba”,我不会将子序列“a”计算两次),则不更新Trie树。这样,如果我发现一个子序列在t_A中出现了n次(其中n是A的大小),我检查它是否在t_B中,如果不在,则计数。但是这非常慢,如果A和B的大小为15,并且字符串长度约为100个字符,我的程序运行时间超过1秒。
编辑:由于任何子序列都以字符串的最后一个字符或其之前的字符结尾,因此我们不必生成所有子序列,而只需要生成以字符串的最后一个字符结尾的子序列。当我将它们推入Trie树时,我用1标记每个节点。因此,如果我有字符串“abcd”,我只推入“abcd”,“bcd”,“cd”和“d”,因为这应该是Trie树的“骨架”。但是这不是一个非常大的优化,我仍在寻找更好的方法。

我并不惊讶你的解决方案有些慢,因为你描述的算法运行时间是n^2级别的。通常来说,像这样的问题,动态规划是一个不错的方法。但是从算法的角度来看,子序列问题非常难解决,所以n^2可能是你所能期望的最好结果。 - pg1989
是的,n^2 是我能想到的最好的方法了,然后我考虑了一种优化方式,因为任何子序列都以字符串的最后一个字符或其前面的字符结尾,所以现在我不再生成所有的子序列,而是只生成以字符串的最后一个字符结尾的子序列,并且当我将它们推入 trie 中时,我会标记每个节点为 1,如果它是新的,或者如果它已经存在,则增加它。因此,如果我有字符串 "abcd",我只会推入 "abcd"、"bcd"、"cd" 和 "d",因为这应该是 trie 的“骨架”。但这并不是一个非常大的优化,我仍在寻找更好的方法。 - Robert Badea
我认为最好称呼那些子字符串而不是子序列。子序列是我们唯一拥有的一个词,用于表示可以通过删除某些元素而不改变其余元素顺序从另一个序列中派生出来的序列。 - Thomas Ahle
1个回答

3

您不必将A中所有字符串的所有子序列都放入trie树中。 只需放入有效的。在添加前测试序列是否有效。我假设成员资格测试比添加新项快。较小的trie应该更快地失败于成员资格测试,因此该策略旨在尽快减少trie的大小。

具体而言: 将A中第一个字符串的所有子序列放入trie树中。(为了效率,请使用最短的字符串作为第一个字符串)。保持对所有叶节点的引用集。 接下来,对于B中的所有字符串,测试每个子序列以查看它是否存在于A中。如果是,则删除该序列及其引用。(从B中最长的字符串开始,以尽快削减trie)。

现在您拥有最小的可能性测试集。 对于A中剩余的所有字符串,测试每个子序列以查看它是否存在于trie中。如果是,则将节点标记为有效,否则移动到下一个子序列。 每个字符串后,从trie中删除所有无效节点,并将其余节点的标志重置为无效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接