【发布时间】:2011-05-28 13:11:56
【问题描述】:
我最近参加了一次 C++ 技术面试,在那里我得到了一些简单的字符串操作代码,该代码旨在获取一个字符串并返回一个由第一个和最后一个 n 个字符组成的字符串,然后继续为了更正任何错误并使功能尽可能高效,我想出了下面的解决方案,但是面试官声称有一个更快更优化的方法:
原码:
std::string first_last_n(int n, std::string s)
{
std::string first_n = s.substr(0,n);
std::string last_n = s.substr(s.size()-n-1,n);
return first_n + last_n;
}
我的代码:
bool first_last_n(const std::size_t& n, const std::string& s, std::string& r)
{
if (s.size() < n)
return false;
r.reserve(2 * n);
r.resize(0);
r.append(s.data(),s.data() + n);
r.append(s.data() + (s.size() - n), s.data() + s.size());
return true;
}
我的更改总结:
更改了接口以将返回字符串作为参考(假设 RVO 和右值尚不可用)
删除了正在通过 substr 构造的临时字符串
将输入字符串作为 const 引用传递,以绕过输入的临时实例化
修复了 last_n 字符串中的 off-by-1 错误
将每个角色的触地次数减少到一次或两次(在重叠场景的情况下)
在字符串 s 的大小小于 n 的情况下进行检查,失败返回 false。
假设只允许使用本机 C++,是否有其他方法可以更有效或最佳地完成上述操作?
注1:原始输入字符串实例不可修改。
注意2:所有解决方案必须通过以下测试用例,否则无效。
void test()
{
{
std::string s = "0123456789";
std::string r = first_last_n(10,s);
assert(r == "01234567890123456789");
}
{
std::string s = "0123456789ABC0123456789";
std::string r = first_last_n(10,s);
assert(r == "01234567890123456789");
}
{
std::string s = "1234321";
std::string r = first_last_n(5,s);
assert(r == "1234334321");
}
}
【问题讨论】:
-
您只能猜测(或物理测量)哪个是最佳的,因为它们都会受到 std::string 的实际底层实现的影响。我个人更喜欢第一个,因为它直观易读。但如果这是为在库中重用而设计的(如 STL),我会选择第二个(如果我能证明有一些真正的优势)。第二个的优点是使用保留来确保不涉及太多复制。 另请注意:复制回字符串并不像您想象的那样昂贵,因为大多数实现都使用写时复制。
-
@Zenikoder:实际上,在任何“正常”C++ 库实现上都可以正常工作,但您应该知道,
c_str()并不要求您返回原地表示string - 允许复制,在这种情况下,您对它的多次调用会使事情变慢。 (这样做的动机是理论上字符串库可能不会以空值终止其内部表示。我有 99% 的信心没有库会这样做,但因为这意味着打开蠕虫的内存管理罐。) -
@j_random_hacker:我同意关于 c_str 的规则,但在实践和现实中,有没有比现在更快的实现 c_str 的方法?
-
@Martin:我不认为 gcc 4.4 或 vs 9.0 或 vs 10.0 是“实验性”编译器——它们在 std::string 实现中不支持 COW,支持 std: :string 导致的效率低下和问题比它解决的要多,现在已经有一段时间了,内存很便宜 yada yada。顺便说一句,马丁如果你认为有一个 STL,你能否提供一个版本和可能的文件/行号,我可以快速查看一下,我可以访问大多数可用的 C++ 编译器(不包括 hpux 平台)跨度>
-
@Mike Dunlavey:这些问题并不是针对像你这样的“脾气暴躁的老袜子”......:D
标签: c++ optimization string processing-efficiency