【问题标题】:Word Frequency Statistics词频统计
【发布时间】:2012-01-30 15:52:37
【问题描述】:

在面试前,我遇到了这样一个问题:

给定一个字符串由一个空格分隔的单词组成,按单词在字符串中出现的次数降序打印。

例如,“a b b”的输入字符串将生成以下输出:

b : 2
a : 1

首先,我想说的是,输入字符串是由单字母词还是多字母词组成还不是很清楚。如果是前者,那就简单了。

这是我的想法:

int c[26] = {0};
char *pIn = strIn;

while (*pIn != 0 && *pIn != ' ')
{
    ++c[*pIn];
    ++pIn;
}

/* how to sort the array c[26] and remember the original index? */

我可以获得输入字符串中每个单字母单词的频率统计信息,并且可以对其进行排序(使用 QuickSort 或其他方式)。但是count数组排序后,如何获取count关联的单字母词,以便以后成对打印出来呢?

如果输入字符串由多个字母组成,我计划使用map<const char *, int> 来跟踪频率。但同样,如何对 map 的键值对进行排序?

问题是用 C 或 C++ 编写的,欢迎提出任何建议。

谢谢!

【问题讨论】:

  • 在预采访中,我不会使用这样的代码。而且我不会这么问。
  • @Qiang Xu:你的编码风格非常C-ish,但不是很好。你应该更喜欢更长更有意义的变量名,我个人会放弃匈牙利符号。

标签: c++ c word-frequency


【解决方案1】:

我会使用std::map<std::string, int> 来存储单词及其计数。然后我会用这个来得到单词:

while(std::cin >> word) {
    // increment map's count for that word
}

最后,你只需要弄清楚如何按频率顺序打印它们,我将把它留给你作为练习。

【讨论】:

  • 谢谢,埃文。我也觉得这里需要一张地图。但我坚持如何按频率顺序打印单词。地图的键总是有序的,而不是它的值。 :-(
  • @QianqXu:你试过什么?您是否考虑过看似低效的解决方案?弄清楚如何让它发挥作用,然后找出如何让它变得高效。想到的一件事可能是第二个std::map<int, std::string>(注意键/值交换),您可以根据第一个std::map<>创建它...
  • 从头开始,第二个地图解决方案不起作用(您将有重复的键),但 std::vector<std::pair<int, std::string>> 似乎是合理的。
  • 编译好像通过了,但是很多链接错误:pastebin.com/QjnMK2sY
  • g++而不是gcc编译c++代码。 g++ 将在 c++ 标准库中正确链接...
【解决方案2】:

您假设您只需要 26 个选项肯定是错误的,因为您的雇主也希望允许使用多个字符的单词(甚至可能是数字?)。

这意味着您将需要一个可变长度的数组。我强烈建议使用矢量,甚至更好的是地图。

要查找字符串中的字符序列,请查找您的当前位置(从 0 开始)和下一个空格的位置。然后就是这个词。将当前位置设置为空格,然后再做一次。不断重复此操作,直到结束。

通过使用地图,您已经拥有可用的字数/计数。

如果您申请的工作需要大学技能,我强烈建议您通过添加某种散列函数来优化地图。但是,从问题的难度来看,我认为情况并非如此。

【讨论】:

  • 谢谢,汤姆!使用地图,计数几乎是微不足道的。但似乎地图最难的部分是按其值的降序打印出它的键值对。绞尽脑汁……
  • 啊,好吧,那是您可能必须实现自己的排序算法或其他东西的部分。可以在 ~15 行内完成。
【解决方案3】:

以C语言为例:

我喜欢蛮力、直接的算法,所以我会这样做:

  1. 对输入字符串进行标记以提供未排序的单词数组。实际上,我必须实际移动每个单词(因为每个单词的长度都是可变的);我认为我需要一个 char* 数组,将其用作 qsort() 的 arg。

  2. qsort( )(降序)该单词数组。 (在 qsort() 的 COMPAR 函数中,假设较大的单词是较小的单词,以便数组获得降序排序。)

3.a.遍历现在排序的数组,寻找相同单词的子数组。一个子数组的结束和下一个数组的开始,由我看到的第一个不相同的单词表示。 3.b。当我到达子数组的末尾(或排序数组的末尾)时,我知道(1)单词和(2)子数组中相同单词的数量。

编辑新的第 4 步: 将 char* 保存到另一个数组(称为 array2)中的子数组中的一个单词以及子数组中相同单词的计数。

  1. 当排序数组中没有更多单词时,我就完成了。是时候打印了。

  2. qsort( ) array2 按词频。

  3. 遍历array2,打印每个单词及其频率。

我完成了!我们去吃午饭吧。

【讨论】:

    【解决方案4】:

    我之前的所有答案都没有给出真正的答案。

    让我们考虑一个潜在的解决方案。

    有一种或多或少的标准方法来计算容器中的东西。

    我们可以使用std::mapstd::unordered_map 之类的关联容器。在这里,我们将一个“键”(在本例中为单词)与一个计数与一个值(在本例中为特定单词的计数)相关联。

    幸运的是,这些地图有一个非常好的索引operator[]。这将查找给定的键,如果找到,则返回对该值的引用。如果未找到,则它将使用密钥创建一个新条目并返回对新条目的引用。因此,在这两种情况下,我们都会获得对用于计数的值的引用。然后我们可以简单地写:

    std::unordered_map<char,int> counter{};
    counter[word]++;
    

    这看起来非常直观。

    经过这个操作,你已经有了频率表。按键(单词)排序,使用std::map 或未排序,但使用std::unordered_map 可以更快地访问。

    现在您想根据频率/计数进行排序。不幸的是,这在地图上是不可能的。

    因此,我们需要使用第二个容器,例如 ```std::vector`````,然后我们可以为任何给定的谓词排序 unsing std::sort,或者,我们可以将值复制到容器中,就像 std::multiset 隐含地对其元素进行排序。

    为了取出std::string 的单词,我们只需使用std::istringstream 和标准提取运算符&gt;&gt;。没什么大不了的。

    由于为 std 容器编写了所有这些长名称,我们创建了别名,使用 using 关键字。

    在这一切之后,我们现在编写超紧凑的代码,只需几行代码即可完成任务:

    #include <iostream>
    #include <string>
    #include <sstream>
    #include <utility>
    #include <set>
    #include <unordered_map>
    #include <type_traits>
    #include <iomanip>
    
    // ------------------------------------------------------------
    // Create aliases. Save typing work and make code more readable
    using Pair = std::pair<std::string, unsigned int>;
    
    // Standard approach for counter
    using Counter = std::unordered_map<Pair::first_type, Pair::second_type>;
    
    // Sorted values will be stored in a multiset
    struct Comp { bool operator ()(const Pair& p1, const Pair& p2) const { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; } };
    using Rank = std::multiset<Pair, Comp>;
    // ------------------------------------------------------------
    
    std::istringstream text{ " 4444 55555 1 22 4444 333 55555 333 333  4444  4444 55555  55555 55555 22 "};
    
    int main() {
    
        Counter counter;
        
        // Count
        for (std::string word{}; text >> word; counter[word]++);
    
        // Sort
        Rank rank(counter.begin(), counter.end());
    
        // Output
        for (const auto& [word, count] : rank) std::cout << std::setw(15) << word << " : " << count << '\n';
    }
    

    【讨论】:

      猜你喜欢
      • 2019-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-28
      • 2011-02-22
      • 1970-01-01
      • 2011-06-25
      相关资源
      最近更新 更多