【问题标题】:How can the runtime efficiency of this function be improved?如何提高这个函数的运行时效率?
【发布时间】:2020-02-03 15:13:08
【问题描述】:

我正在尝试编写用于删除句子中重复单词的最佳代码(良好的运行时效率)。

例如,函数的输入字符串Jack Juliet Juliet Jill Jack Romeo Jack Jill应该返回Jack Juliet Jill Romeo

以下是我的代码:

std::string removeDuplicateWords(const std::string& str)
{
    std::stringstream ss (str);
    std::unordered_set<std::string> string_history;
    std::string current_string, output_string;
    ss >> current_string;
    string_history.insert(current_string);
    output_string += current_string;
    while(ss >> current_string) {
        if(string_history.find(current_string) == string_history.end()) {
            output_string += " " + current_string;
            string_history.insert(current_string);
        }
    }
    return output_string;
}

【问题讨论】:

  • 在每个插入上使用std::move 可能会很棒。您也可以将output_string 更改为stringstream,然后返回output_string.str()
  • 我认为为了更好的可读性,您至少应该使用 标头。另外我建议您查看 range-v3 库。视图可以懒惰地完成它,因此您可能会获得性能提升。
  • @DeiDei 为什么将output_string 更改为stringstream?然后你将不得不复制它的缓冲区。
  • 要回答此类问题,您的出发点应该是为您的程序提供您尝试优化的输入类型(例如,大量单词)并在探查器。这会告诉你时间都花在了哪里,如果那是你优化的目标!

标签: c++ c++11 optimization runtime


【解决方案1】:

对于这个例子,获得性能没有多大意义,如果你只有大约 10 个单词要处理,请使用更简单的代码。不过,您可以采取一些措施来提高性能。

首先,您不想检查元素是否在集合中。 insert 返回一对 iterator-bool,其中第一个是带有键(现有或新)的元素的迭代器,第二个是指示插入是否发生的布尔值。

      if (string_history.insert(current_string).second)
          output_string += " " + current_string;

这既简化了代码又提高了性能。正如 cmets 中已经指出的那样,使用std::move 是有道理的,但是,如果将它与插入一起使用,则需要迭代器来获取移动到的对象。 对于这种情况,它无济于事,就像您在 SSO 情况下一样。

需要更多代码的第二件事是删除std::stringstream。流会产生一些开销。相反,您可以更好地接受std::string_view 并使用子字符串逻辑将其切成碎片。这可以防止创建新的字符串对象并消除流的开销。您确实需要 C++17(或提供此功能的库)。

最后,你可以在开头保留output_string,你知道最大大小(也就是str的大小)。如果您最终超出 SSO 范围,这可以防止重新分配。

【讨论】:

    【解决方案2】:

    改进:

    • 您无需检查字符串是否存在,因为 unordered_set 会为您完成。
    • current_string 被移动,因为我们不再需要它。

      std::string removeDuplicateWordsImproved( const std::string& str )
      {
          std::stringstream ss (str);
          std::unordered_set<std::string> string_history;
          std::string current_string, output_string;
      
          while( ss >> current_string )
              string_history.insert( std::move( current_string ) );
      
          for ( auto& word : string_history )
              output_string.append( std::move( word ) ).push_back( ' ' );
      
          output_string.erase( output_string.size() - 1 );
      
          return output_string;
      }
      

    【讨论】:

    • 与原始版本不同的是,您重新排序输出,而不是与输入相比保持稳定。
    • 是的,你是对的,我认为这不是问题。
    • 如果重新排序是一个问题,那么@JVApen 的答案可能是首选。
    • 输出字符串的顺序必须与输入字符串的顺序一致
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-19
    • 2019-05-01
    相关资源
    最近更新 更多