【问题标题】:Vector of strings with a counter C++ [closed]带有计数器 C++ 的字符串向量 [关闭]
【发布时间】:2017-09-11 18:36:25
【问题描述】:

我正在输入一个字符文件,每个单词在向量中都有自己的位置。然后我需要跟踪每个单词并找出每个唯一单词出现的次数,这样:

三棵树树树

应该输出:

那里 1 是 1 三 1 树木 3

我想知道如何使用字符串向量来记录每个单词。 我会做一个字符串向量,每个字符串都有一个单一的 int 向量吗?

【问题讨论】:

  • 为什么不使用std::map
  • unordered_map<string, int> 可能是您想要的。 (unordered_multiset,再看一遍,有些奇怪;为什么 count 在匹配项的数量上是线性的?)
  • 关联容器并非易事。看来您仍然需要跟踪单词在输入中出现的顺序。
  • @FrançoisAndrieux 目前还不清楚这是否是一个要求,但如果是,那么它仍然很简单,只需保留一个vector<string>,并且每次新字符串还没有出现在关联容器,push_back 到向量中。
  • 这个问题需要大量澄清。您只是想从标准输入中打印不同单词的 频率 吗?这仅在几行代码中就相当简单了,其中包含从字符串到计数器的适当管理的关联容器。跟踪每个单词出现的where 只涉及一点点。但是,维护输出的输入顺序相当要复杂得多。

标签: c++ vector counter


【解决方案1】:

不要用螺丝刀钉钉子。 std::vector,对于这项任务的最基本形式并不是特别有用:简单的频率计算。来自标准输入的任意输入最好利用关联容器,其中键是输入字符串,值是累积频率。

无序频率计算

无序映射类std::unordered_map 键入std::string 并映射到该字符串的频率计数器,可用于跟踪基本频率。例如:

#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>

int main()
{
    std::unordered_map<std::string, unsigned> m;
    std::string word;
    while (std::cin >> word)
        ++m[word]; // increment the count for this word

    for (auto const& pr : m)
        std::cout << pr.first << ':' << pr.second << '\n';
}

按字典顺序排列的频率

注意:使用关联容器std::unordered_map(因此得名)没有特定的顺序。如果您需要字典顺序,您可以简单地使用常规的std::map。如:

#include <iostream>
#include <vector>
#include <string>
#include <map>

int main()
{
    std::map<std::string, unsigned> m;
    std::string word;
    while (std::cin >> word)
        ++m[word];

    for (auto const& pr : m)
        std::cout << pr.first << ':' << pr.second << '\n';
}

位置留存频率计算

在计算频率计数器时维护输入流中出现单词的位置也是可能的,并且只需要多一点代码。像以前一样选择无序或有序关联容器,但不是映射到unsigned,而是映射到std::vector&lt;unsigned&gt;,我们在使用输入单词时累积一个单词计数器。每个向量的整体大小仍然保留频率计数器,但向量本身保留相关单词出现在输入流中的位置。例如:

#include <iostream>
#include <vector>
#include <string>
#include <map>

int main()
{
    std::map<std::string, std::vector<unsigned int>> m;
    std::string word;
    unsigned ctr = 0;
    while (std::cin >> word)
        m[word].push_back(++ctr);

    for (auto const& pr : m)
    {
        std::cout << pr.first << ':' << pr.second.size() << " { ";
        for (auto pos : pr.second)
            std::cout << pos << ' ';
        std::cout << "}\n";
    }
}

这将产生以下形式的输出:

word : frequency { n1 n2 n3... }

其中word 是一个不同的单词,frequency 是输入流中的总频率,n1,n2,n3,... 是处理过程中单词出现的位置(从 1 开始)。

希望其中一种方法有用。

【讨论】:

    【解决方案2】:

    您可以使用 c++ 中的 multiset 类,它将跟踪您将每个单词添加到集合中的次数。另外请记住,您可以从 C++ 中的流中读取完整的单词,它会自动跳过任何空格字符。

    我将从标准输入读取这个例子(注意,我没有编译这个,只是为了展示这个想法)。

    #include <set>
    using namespace std;
    
    int main(){
      string word;
      multiset<string> ocurrences;
      while(cin >> word){
        ocurrences.insert(word);
      }
      for(string w : ocurrences){  // Iterate over all words in the set
        cout<<w<<" "<<counts.count(w)<<" ";
      }
    }
    

    如 cmets 中所述,如果您想按第一次出现的顺序打印单词,只需保留 vector&lt;string&gt; 并添加您读取的每个单词(如果它尚未在集合中),然后迭代此向量而不是集合。

    #include <set>
    using namespace std;
    
    int main(){
      string word;
      vector<string> words;
      multiset<string> ocurrences;
      while(cin >> word){
        if(ocurrences.count(word) == 0) //Is this the first time we see this word?
          words.push_back(word);
        ocurrences.insert(word);
      }
      for(string w : words){ //Iterate over the words in the order
                             //they appeared in the input.
        cout<<w<<" "<<ocurrences.count(w)<<" ";
      }
    }
    

    另一件事,即使多重集更适合解决此特定问题,您在问题中询问的内容称为映射,一种将键与值(可能是不同类型)相关联的数据结构。 C++ already has a map implementation。在这种情况下,您需要map&lt;string, int&gt; 将每个单词与其出现的时间相关联。

    【讨论】:

    • 我阅读了 C++ 中的multi*set 数据结构,我意识到我并不真正理解它们。似乎它们旨在存储 比较 相等但不相同的事物的多个实例。 countmultiset 的总项目数为对​​数,与找到的项目数成线性关系。除非它存储插入元素的所有副本,否则没有理由会出现这种情况。在这种情况下,multi_* 结构不适合计算真正相同的事物。
    • 哇,感谢您指出这一点,我基本上是根据它的接口选择数据结构,我有点假设它是在内部实现为一个统计出现次数的地图。
    【解决方案3】:

    这是一种方法,您可以通过在单词流上累积字典并使用 C++17 结构化绑定来做到这一点:

    int main()
    {
        std::istringstream words( "There are three trees trees trees" );
    
        auto dic = std::accumulate(
            std::istream_iterator< std::string >( words ) ,
            std::istream_iterator< std::string >( ) ,
            std::unordered_map< std::string , int >( ) ,
            []( auto && map , auto && word ) -> decltype( auto )
            {
                auto [ it , success ] = map.try_emplace(
                    std::forward< decltype( word ) >( word ) , 0 );
    
                ++ it->second;
    
                return std::forward< decltype( map ) >( map );
            } );
    
        for ( const auto & [ key , value ] : dic )
        {
            std::cout << key << ": " << value << std::endl;
        }
    }
    

    Live at Coliru(虽然有一些警告)

    > trees: 3
    > three: 1
    > There: 1
    > are: 1
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-01
      • 2014-11-16
      • 1970-01-01
      相关资源
      最近更新 更多