我正在做一个算法来检查一个给定的字符串是否是回文的排列(例如“Tact Coa”是“taco cat”的排列)。我的算法忽略空格并且不区分大小写。以下是我的实施:
bool isPalindromePermutation(string str) {
set<char> s;
// Convert to lower case
for (int i = 0; i < str.size(); i++) {
if (str[i] >= 65 && str[i] <= 90) {
str[i] += 32;
}
}
for (int i = 0; i < str.size(); i++) {
// find() is O(log n)
bool contains = s.find(str[i]) != s.end();
// Ignore whitespace
if (str[i] != ' ') {
if (contains) {
s.erase(str[i]);
}
else {
s.insert(str[i]);
}
}
}
return s.empty() || s.size() == 1;
}
我到目前为止的时间复杂度是O(n log n),因为我遍历字符串的每个字符(即O(n)),并且对于每个字符,我在集合中查找它是O(log n),给我O(n log n)。 令我困惑的部分是如何处理条件。我是否必须假设它们总是被执行?
另外,我知道集合insert()
的复杂性为O(log n)。但我对erase()
方法(阅读here)
这种算法的复杂性是什么?
答案 0 :(得分:1)
如何治疗条件?
如果你想要O,你试图计算一个上限。在这里,我简单地假设最坏的情况是总是调用一个或另一个分支。然后,确定每个分支的相应复杂性。两个分支的上限是两个分支的最大复杂度,简单如此。
在您的情况下,您可以更进一步:对集合中的元素数量有固定的限制K.此外,如果在一次迭代中插入元素,则具有相同值的下一次迭代将再次擦除该元素。因此,在集合中插入K元素后,您就会知道下一次迭代必须再次删除元素。这意味着,如果只进行足够的迭代,则会有相似数量的插入和删除,它们仅相差K,这可以忽略不计。如果两个分支是O(a)和O(b),则组合复杂度因此为O((a + b)/ 2 + c)= O((a + b)/ 2)。
擦除操作的复杂程度是什么?
您已找到该文档。再次检查它,它是erase()
调用的第三种形式,即“log(c.size())+ c.count(k)”。 “c.count(k)”就是其中之一。 “log(c.size())”取决于集合的最大大小,集合的大小受其类型的限制,在本例中为char
,2 ^ CHAR_BITS。其对数是CHAR_BITS,通常为8.总之,复杂性是恒定的。
算法的整体复杂性是什么?
你几乎是自己找到它,你只是错过了这个集合的大小被限制为一个不依赖于输入大小的值的点。当n是输入序列的大小时,这会使插入,查找和擦除O(1),从而使整个算法成为O(n)。