【问题标题】:Efficient algorithm to check for subset against a range of sets针对一系列集合检查子集的有效算法
【发布时间】:2016-04-23 12:39:52
【问题描述】:

我阅读了一些关于确定集合A 是否是另一个集合B 的子集的帖子。但我发现很难确定使用什么算法。以下是问题的概要:

  • 我有一个字符串数组A,我在程序开始时收到它。对结构知之甚少。数组中的每个字符串可以任意长,并且条目的数量不受限制。虽然通常可以假设数组中的条目数不会太大(
  • 然后我遍历长度为n 的对象列表。
  • 每个n 对象还将有一个字符串数组B,即会有n B 数组。一旦程序运行,Bs 将被修复,即它们在运行时不会改变。
  • 我想为每个对象确定A 是否是B 的子集。

现在,我想到了哈希表。但是,在我看来,它们只有在只有一个 B 和很多 As 时才会有效。然后我可以为B 制作一个哈希表,并根据我的哈希表检查每个对象的每个字符串数组。但事实并非如此,因为只有一个An Bs。什么是执行此操作的有效算法?

例子:

A:  ["A", "G", "T"]
B1: ["C", "G"]
B2: ["K", "A", "U", "T", "G"]
.
.
.
Bn: ["T", "I", "G", "O", "L"]

这里AB2 的子集,但不是B1 的子集,也不是Bn 的子集。

【问题讨论】:

  • 您需要多久检查一次 A 中的某个字符串是否是 B 的子字符串?您的程序是否运行了很长时间,在此期间您多次检查(可能已更改)B 的相同字符串列表 A?
  • 在程序的每次运行中,A 都固定在开头。 B 也在程序运行期间修复。它们不会改变。但是有很多Bs。
  • “而 B 因对象而异”所以如果 B 是固定的,那意味着什么。您能否编辑您的答案并发布一些 A 和 B 的示例数据?
  • 你能告诉我们关于字符串长度的分布吗? (在您的示例中,它们都是 1。)
  • 对不起,我对这个答案不满意。字符串是短的、长的还是很长(1 兆字符)?他们都是 ? ...

标签: algorithm set subset


【解决方案1】:

正如你之前知道的A,你可以设计a collision-free hash function来散列A的所有元素。

然后在搜索步骤中仅对哈希进行操作,而不是对字符串进行操作。对于 B 的每个元素,计算其哈希值,然后用它来查找 A 的一个元素。如果找到一个元素,则表示哈希值匹配;那么您还需要比较字符串以检测它是真正的肯定还是只是偶然的匹配。

计算匹配的数量。当该数字等于 A 的大小时,停止并返回一个肯定的结果。如果 B 的所有元素都已处理完毕,并且匹配的数量小于 A 的大小,则返回否定结果。

【讨论】:

  • 如果 B 中的元素是 A 的严格超集,您如何检测 A 是 B 中元素的子集?他们的哈希值不会一样吧?
  • @Glubus 哦,我误读了这个问题。更新了现在描述简单算法的答案。我认为可以做得更好。
  • 是的,我现在明白你的意思了,这更有意义。我确实喜欢 CiaPan 提出的二进制搜索的想法,但如果 A 比某个 B 小得多,那么搜索可能比遍历整个列表更有效。
【解决方案2】:

一种有效的方法是将集合 A 表示为一个 trie。这允许检查给定的字符串是否在时间上线性地属于字符串长度。

那么没有更好的方法比彻底检查所有 Bi 和 Bi 中的所有字符串是否属于 A。一旦匹配 A 中的所有字符串(当找到字符串时标记一个字符串),搜索就会停止。

运行时间将与所有B中所有字符串中的字符总数成正比。实际上,很大一部分字符会被跳过,因为

  • 搜索不在 A 中的字符串可以提前终止,

  • 即使Bi中还有字符串,子集测试也能得出肯定的结论,

  • 当 A 中不匹配的字符串多于 Bi 中剩余的字符串时,子集测试可以得出否定结论。

这种方法无疑是最坏情况下的最佳选择,因为您最多只能读取一次字符并且每个字符执行固定数量的操作。

【讨论】:

  • 嗯,我喜欢这种方法。我最近刚刚实现了一个 trie,所以它也派上用场了。
  • 我只是在想。通过使用三元搜索树,它可能会更加节省空间。
  • 是的,如果您需要空间效率,可以使用更紧凑的 try 变体。但在字母词的情况下,一个 trie“仅”消耗每个字母 26 个指针。
【解决方案3】:

作为第一种方法,我会预先计算集合的一些一般属性,这(希望)可以让您快速过滤一些Bs。例如,这些可能是:

  • 多个字符串——如果A 包含的元素多于B,则它肯定不能是B 的子集;
  • 最长字符串的长度——如果A 中的最长字符串比B 中的最长字符串长,A 肯定不是B 的子集;
  • 字符串长度的总和。

为了便于检查,您可能需要按字母顺序对每个集合进行排序。这将允许在通过两组字符串的(线性)扫描中检查 A 和单个 B

对于小的A 和大的B 集合,使用二分搜索而不是线性扫描在B 中查找字符串可能更有效;这也需要对B 进行预排序。

【讨论】:

  • 所有这些预先计算都应该完成,因为实际算法是以某种启发式的形式运行的,因为在运行某些算法之前需要对潜在的大输入进行多次循环。如果 A 比 B 中的任何元素小得多,二分搜索也会非常简洁。
  • @Glubus 所有这些参数只需要对每个集合进行一次扫描,每个字符串只需进行一次长度测试,因此它具有严格的线性时间成本和最小内存成本(每个字符串集存储三个整数)。 // 是的,正如我上面所说,二分查找对于大小A 比大小B 更有意义。对于可比较的尺寸,半平行线性扫描会更容易。但是二分查找需要对B 中的字符串进行排序,这可能会导致额外的排序成本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-01
  • 1970-01-01
  • 2019-03-12
  • 2016-07-16
  • 2015-12-06
相关资源
最近更新 更多