提问人:Roofin88 提问时间:10/31/2023 最后编辑:Roofin88 更新时间:11/1/2023 访问量:159
如何提高加扰代码的性能?
How can I improve performance of scramble code?
问:
我需要完成函数 scramble(str1, str2),如果可以重新排列 str1 字符以匹配 str2,则返回 true,否则返回 false。通过哪种方法可以提高此代码的性能? 例如,如果我在 str1 和 str2 中有 200k+ 个字符,则性能非常糟糕。
bool scramble(const string& s1, const string& s2)
{
string reference = s2;
string inputStr = s1;
size_t refLen = reference.length();
size_t lenCounter = 0;
for (auto n = 0; n < reference.length(); n++)
{
auto tempLetter = reference[n];
size_t letterPos = inputStr.find(tempLetter);
if (letterPos != string::npos)
{
lenCounter++;
inputStr.erase(letterPos, 1);
if (lenCounter == refLen)
{
return true;
}
}
else
return false;
}
if (refLen == lenCounter)
return true;
else
return false;
}
这是一个现成的解决方案,可以满足我的要求。@Dave提供的此解决方案的想法
bool scramble(const string& s1, const string& s2)
{
string ref = s2;
string input = s1;
string result = "";
char tempLetter = '\0';
sort(ref.begin(), ref.end());
sort(input.begin(), input.end());
map<char, int> letters;
for (auto n = 0; n < input.length(); n++)
{
tempLetter = input[n];
map<char, int> ::iterator it = letters.find(tempLetter);
if (it == letters.end())
{
letters.insert(make_pair(tempLetter, 0));
letters.at(tempLetter) = 1;
}
else
{
letters.at(tempLetter) += 1;
}
}
for (auto i = 0; i < ref.length(); i++)
{
tempLetter = ref[i];
map<char, int> ::iterator it = letters.find(tempLetter);
if (it != letters.end() && letters.at(tempLetter) >=0)
{
letters.at(tempLetter) -= 1;
if (letters.at(tempLetter) < 0)
return false;
result.push_back(tempLetter);
}
}
if (result!=ref)
return false;
else
return true;
}
答:
1赞
Avi Lachmish
10/31/2023
#1
出于两个原因,我会写得有点不同:
- 可读性和可维护性
- 复杂性
比方说,那应该更大.m = sizeof s1;
n = sizeof s2;
n
m
首先对字符串进行排序将花费 O(nlog(n)),但随后,在一次传递中,您可以获得输出,这意味着我的加扰函数复杂度为:
O(n+nlogn) = O(nlongn)
但是,在您的情况下,复杂性是 .O(n*m)
我是这样写的:
bool is_scramble(std::string s1, std::string s2)
{
if (s2.size() < s1.size()) return false;
std::ranges::sort(s1);
std::ranges::sort(s2);
return std::ranges::includes(s2, s1);
}
正如评论中提到的,您可以进行计数排序(在 ASCII 或 EBCDIC 的限制下),这在时间复杂度方面甚至更便宜,然后编写以下代码。
它确实需要 256 字节的额外存储空间(有 ASCII 或 EBCDIC 的限制)。恕我直言,它的可读性也较差。
bool is_scramble2(std::string s1, std::string s2)
{
// s1 needs to be contain in s2
if (s2.size() < s1.size()) return false;
constexpr int maxChar = 256; // Assuming ASCII character set
std::array<int, maxChar> count = {0};
// Count the occurrences of each character in the string
for (char c : s2) {
count[static_cast<int>(c)]++;
}
for(const auto c : s1) {
if (count[static_cast<int>(c)] <= 0) return false;
count[static_cast<int>(c)]--;
}
return true;
}
评论
0赞
Jarod42
10/31/2023
使用计数排序甚至可以(甚至不需要排序部分,因为只有计数部分就足以检查包含)。O(256 * n)
0赞
Avi Lachmish
10/31/2023
是的,你是对的,但你假设 ASCII 字符并且你使用了 256 个额外的字节
0赞
Jarod42
10/31/2023
我不认为 ascii(EBCDIC 也会得到支持)。
0赞
Avi Lachmish
10/31/2023
对不起,你又是对的......将更新我的答案
1赞
Dave
10/31/2023
#2
如果字符串的长度不同,则返回 false。
使用计数哈希。
对于第一个单词,递增地图中每个字母的计数。对于第二个,递减每个字母的计数。
递减时,如果字母递减到零以下,则返回 false。如果坚持到最后,则返回 true。
O(n) 表示 n 个字母。
您可以使用一个整数数组,在相关字母表中每个字符有 1 个索引,而不是计数哈希,并将其用于计数。
评论
2赞
Roofin88
11/1/2023
非常感谢,你的回答真的帮助了我。我已经针对我的情况进行了优化,效果很好。也感谢您没有提供现成的解决方案,这对我的 C++ 学习非常好)
1赞
Ben Voigt
11/1/2023
“最后,如果总数没有回落到零,则返回 false”,或者提前退出 if ,在这种情况下,您根本不需要进行任何散列。a.size() != b.size()
0赞
Dave
11/1/2023
@BenVoigt 好点子。编辑。谢谢。
0赞
Vlad Feinstein
11/3/2023
你真的需要地图吗?我会为每个字母使用一组计数器。
1赞
Dave
11/3/2023
@VladFeinstein 这就是我最后一句话的意思:使用一个整数数组,而不是计数哈希,每个字符有 1 个索引。这还不清楚吗?
评论