O(N)排列的识别

时间:2016-04-26 12:41:20

标签: c++ big-o permutation string-comparison standard-library

This answer通过比较它们的内容来确定两个字符串是否是排列。如果它们包含相同数量的每个字符,则它们显然是排列。这是在 O(N)时间内完成的。

我不喜欢这个答案,因为它重新发明了is_permutation的目的。也就是说,is_permutation的复杂性为:

  

谓词的最多 O(N 2 应用程序,如果序列已经相等,则完全 N ,其中{{1 }}

所以我不能提倡使用N=std::distance(first1, last1),它比手动旋转算法慢几个数量级。但是,该标准的实施者肯定不会错过这种明显的改进吗?那么为什么is_permutation O(N 2

3 个答案:

答案 0 :(得分:9)

is_permutation适用于几乎所有数据类型。链接中的算法仅适用于具有少量值的数据类型。

这与std::sort为O(N log N)的原因相同,但计数排序为O(N)。

答案 1 :(得分:7)

是我写了那个答案。

当字符串的value_typechar时,查找表中所需的元素数为256.对于双字节编码,为65536.对于四字节编码,查找表将有超过40亿条目,可能大小为16 GB!其中大部分都没用过。

首先要认识到即使我们将类型限制为charwchar_t,它仍然可能无法维持。同样,如果我们想对is_permutation类型的序列执行int

对于大小为1或2字节的整数类型,我们可以对std::is_permutation<>进行专门化。但这有点让人想起std::vector<bool>,回想起来并不是每个人都认为这是一个好主意。

我们也可以使用基于std::map<T, size_t>的查找表,但这可能是分配量很大,因此可能不是性能获胜(或者至少,并非总是如此)。但是,为了进行详细的比较,可能值得实施一个。

总之,我没有对C ++标准提出错误,因为is_permutation没有包含char的高性能版本。首先,因为在现实世界中,我不确定它是模板的最常见用法,其次是因为STL不是算法的全部和最终,特别是在领域知识可以是用于加速特殊情况的计算。

如果事实证明is_permutation char在野外非常普遍,那么C ++库实现者将有权为它提供专业化。

答案 2 :(得分:4)

您引用的答案适用于char。它假定它们是8位(不一定是这种情况),因此每个值只有256种可能性,并且您可以便宜地从每个值转到数字索引以用于计数的查找表(对于{{1}在这种情况下,值和索引是相同的东西!)

它生成每个字符串中每个char值出现的次数;那么,如果两个字符串的这些分布相同,则字符串是彼此的排列。

时间复杂度是多少?

  • 你必须走每个字符串的每个字符,所以M + N步骤为两个长度为M和N的输入
  • 这些步骤中的每一步都涉及在char给出的索引处递增固定大小表中的计数,因此是常量时间

因此,总体时间复杂度为O(N + M):线性,如您所述。

现在,char对其输入没有做出这样的假设。它不知道只有256种可能性,或者它们确实是有界限的。它不知道如何从输入值转到它可以用作索引的数字,更不用说如何在恒定时间内这样做。它唯一知道的是如何比较两个值的相等性,因为调用者提供了这些信息。

所以,时间复杂度:

  • 我们知道必须在某个时刻考虑每个输入的每个元素
  • 我们知道,对于之前未见过的每个元素(我将讨论如何确定这一点以及为什么它不会影响大O复杂性作为练习),它无法将元素转换为计数表的任何类型的索引或键,因此它无法计算该元素的出现次数,这比通过两个输入的线性遍历更好看看有多少元素匹配

所以复杂性最多只能是二次方。