这个问题需要注意的一点是,如果字符串中可能出现的不同字符数只有C个,那么对于任何长度大于等于C+1的字符串,你都可以自动返回存在重复项,而无需查看字符串,因为其中的字符太多了,它们不可能全部是唯一的(这就是鸽笼原理在起作用)。这对于思考这个特定问题的结构非常重要。
接下来,请注意您甚至不需要一堆计数器。您只需要每个字符一个比特位,因为在遍历数组时,您只需要知道您是否曾经见过一个字符(1)或从未见过(0)。这意味着您需要每个字符一个比特位。如果您的字长为W,则意味着基于数组的解决方案需要大约C / W个机器字的存储空间。
让我们想象一下,您正在使用C = 256(例如,每个字符都是一个字节值)在一个字长为32位的机器上(W = 32)。这意味着您需要八个机器字来存储位数组,这是一种可以轻松初始化为0的可忽略的存储空间。现在,考虑您的集合实现。如果您使用哈希表,将会使用某种内部数组来存储所有内容。您还需要空间来存储有关哈希函数的信息,并且通常会在某个地方缓存集合的大小。这将占用大约三个机器字仅用于大小和哈希函数信息,剩下五个字的空间。如果哈希表是通用实现的,并且每个条目使用一个机器字,则您的方法仅在哈希表小于或等于四个条目时才能节省空间,这不太可能发生。如果您的哈希表经过优化并直接存储char值,则可以存储多达五个字的char(20个char)而没有任何冲突,但如果您尝试保持负载因子低,则可能在看到10个或更多字符后调整表格大小。因此,简而言之,除非您有一个非常短的字符串,否则哈希表方法可能会使用更多的内存,并且哈希的开销将很高。数组方法可能更快。
另一方面,假设您在字符串中存储任意Unicode字符。现在,C = 1,114,112(感谢维基百科),即使使用64位字长,您需要一个包含17,408个机器单词的数组才能存储可能字符的每个比特位。那是非常大的存储空间,初始化它需要一段时间。现在,如果您输入的字符串是“合理的”,而不是病态构建的,那么很有可能会在字符串中很早就发现重复元素(如果字符串完全随机,则根据生日悖论,平均只需要√(2C)个字符就会得到重复元素),因此构建哈希表可能需要更少的空间。但是,如果字符串是病态构建的,以至于每个字符都是唯一的,那么哈希函数的常数因子开销、哈希表调整大小等等将很可能意味着您的方法比基于数组的方法慢,但这是一个不寻常的用例。
总之:
- 如果可能字符数较小(例如ASCII),则基于数组的方法可能会更快且更节省内存。
- 如果可能字符数较大(例如Unicode),则基于数组的方法可能在合理输入上较慢且内存效率较低,但对于选择病态输入可能比基于哈希的方法更快。
这样说,除非代码在紧密循环中运行,否则除“只使用一个集合”之外的任何其他方法都会使代码难以阅读,而对程序整体效率的最小受益。出于这个原因,一个合理的答案是“除非有理由不使用集合,否则使用集合,并仅在数据支持它的情况下切换到基于数组的方法”。