特里和子序列
Posted
技术标签:
【中文标题】特里和子序列【英文标题】:Trie & subsequences 【发布时间】:2012-03-13 20:14:41 【问题描述】:我们有两个集合,A 和 B。每个集合都包含字符串。 例如:A - “abwcd”、“dwas”、“www” 和 B - “opqr”、“tops”、“ibmd” 如何计算出现在集合 A 中的所有字符串中但没有出现在集合 B 中的所有字符串中的子序列?对于上面的例子,答案是 1(子序列“w”)。
所有这一切都以最佳方式进行。我考虑过使用两次尝试,第一次将 B 中所有字符串的所有子序列放入 trie t_B 中,然后,我开始将 A 中所有字符串的所有子序列放入 trie t_A 中,如果相同则不更新 trie之前在同一个字符串中找到了子序列(例如:如果我有字符串“aba”,我不计算子序列“a”两次)。这样,如果我在 t_A 中找到有 n(A 的大小)出现的子序列,我检查它是否在 t_B 中,如果不是,我计算它。但这非常非常慢,如果 A 和 B 的大小为 15 并且字符串大约 100 个字符长,我的程序运行时间超过 1 秒。
编辑: 由于任何子序列都以字符串的最后一个字符或之前的字符结束,因此我们不必生成所有子序列,而是生成以最后一个字符结尾的子序列的字符串。当我将它们推入特里树时,我用 1 记录每个节点。所以如果我有字符串“abcd”,我只推“abcd”、“bcd”、“cd”和“d”,因为这应该是 '特里的骨架。但这不是一个很大的优化,我还在寻找更好的东西。
【问题讨论】:
我并不惊讶您的解决方案有点慢,您描述的算法的运行时间约为 n^2。经常遇到此类问题,动态编程是一种很好的方法。但是从算法的角度来看,子序列问题是出了名的难,所以 n^2 可能是你所希望的最好的。 是的,n^2 是我能想到的最好的,然后我想到了优化,因为任何子序列都以字符串的最后一个字符或它之前的字符结尾,所以现在我'不是生成所有子序列,而是以字符串的最后一个字符结尾的子序列,当我将它们推入特里树时,我注意到每个节点都有 1 它是新的,或者如果它已经存在则增加它。因此,如果我有字符串“abcd”,我只推送“abcd”、“bcd”、“cd”和“d”,因为这应该是 trie 的“骨架”。但这不是一个很大的优化,我还在寻找更好的东西。 我认为调用这些子字符串而不是子序列更好。子序列是我们唯一可以通过删除一些元素而不改变其余元素的顺序从另一个序列派生的序列的词。 【参考方案1】:您不必将 A 中所有字符串的所有子序列都放入 trie 中。 只放入有效的。在添加之前测试一个序列是否有效。我假设会员测试比添加新项目更快。较小的 trie 应该更快地通过成员资格测试,因此此策略旨在尽可能快地减少 trie。
具体来说: 将 A 中第一个字符串的所有子序列放入 trie 中。 (为了效率,使用最短的字符串作为第一个)。保留一组对所有叶节点的引用。 接下来,对于 B 中的所有字符串,测试每个子序列以查看它是否存在于 A 中。如果存在,则删除该序列及其引用。 (从 B 中最长的字符串开始,以尽可能快地削减 trie)。
现在您可以进行测试的可能性最少。 对于 A 中的所有剩余字符串,测试每个子序列以查看它是否存在于 trie 中。如果是,则将该节点标记为有效,否则移动到下一个子序列。 在每个字符串之后,从 trie 中删除所有无效节点,并将其余的标志重置为无效。
【讨论】:
以上是关于特里和子序列的主要内容,如果未能解决你的问题,请参考以下文章