当前位置:首页GESP > 正文

从不会 map 到轻松解决词频统计:一题吃透两种思路

作者:野牛程序员:2026-03-25 19:37:05GESP阅读 1984

从不会 map 到轻松解决词频统计:一题吃透两种思路(C++详解)

在编程学习中,有一类题目特别适合打基础——看起来简单,但能引出非常关键的思维转变。“统计出现次数最多的单词”就是典型代表。

这篇内容不直接给出最优解,而是按照学习路径一步步展开:

先用最基础的方法解决问题
再引入 map 优化
最后对比两种思路的本质区别

这样理解会更扎实,也更容易迁移到其他题目。


一、题目描述(完整)

输入:

  • 一个整数 n(1 ≤ n ≤ 100)

  • 接下来输入 n 个单词

要求:

  1. 忽略大小写(例如 Apple、apple、APPLE 视为同一个单词)

  2. 找出出现次数最多的单词

  3. 输出该单词(统一为小写)

  4. 保证答案唯一


二、第一种方法:不用 map 的基础解法

思路

既然要统计次数,可以采用最直接的办法:

对于每一个单词,去和后面的所有单词进行比较,统计相同的数量。

在此之前,需要先把所有单词统一转换成小写。


大小写处理(不用 tolower)

if (c >= 'A' && c <= 'Z') {
   c = c - 'A' + 'a';
}

这是利用字符的 ASCII 规律完成转换。


完整代码(基础版)

#include <iostream>
#include <string>
using namespace std;

int main() {
   int n;
   cin >> n;

   string a[105];

   // 输入 + 转小写
   for (int i = 0; i < n; i++) {
       cin >> a[i];
       for (int j = 0; j < a[i].size(); j++) {
           if (a[i][j] >= 'A' && a[i][j] <= 'Z') {
               a[i][j] = a[i][j] - 'A' + 'a';
           }
       }
   }

   int maxCount = 0;
   string ans;

   // 统计
   for (int i = 0; i < n; i++) {
       int count = 1;

       for (int j = i + 1; j < n; j++) {
           if (a[i] == a[j]) {
               count++;
           }
       }

       if (count > maxCount) {
           maxCount = count;
           ans = a[i];
       }
   }

   cout << ans << endl;

   return 0;
}


这个方法的问题

这个写法可以得到正确答案,但存在一个现象:

同一个单词会被多次统计。

例如输入:

apple apple apple

统计过程大致是:

  • 第一次统计得到 3

  • 第二次统计得到 2

  • 第三次统计得到 1

虽然最终结果是正确的,但明显存在重复计算。


为什么结果仍然正确

因为代码只关心最大值:

if (count > maxCount)

第一次已经得到最大值,后面的结果不会覆盖它。


小结

这种方法的特点是:

  • 思路简单直观

  • 不依赖高级工具

  • 存在重复计算

  • 时间复杂度为 O(n²)


三、思路升级:引入 map

思维变化

前面的做法本质是“不断比较”。

现在换一个角度:

每读取一个单词,就直接记录它出现的次数。


map 的作用

map<string, int> cnt;

可以把它理解为一个统计表:

  • 键(key):单词

  • 值(value):出现次数


核心代码

cnt[s]++;

这行代码的含义是:

  • 如果单词不存在,就创建并初始化为 0

  • 然后加 1

完成一次统计


完整代码(map版)

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() {
   int n;
   cin >> n;

   map<string, int> cnt;

   for (int i = 0; i < n; i++) {
       string s;
       cin >> s;

       // 转小写
       for (int j = 0; j < s.size(); j++) {
           if (s[j] >= 'A' && s[j] <= 'Z') {
               s[j] = s[j] - 'A' + 'a';
           }
       }

       cnt[s]++;
   }

   string ans;
   int maxCount = 0;

   // 找最大值
   for (auto &p : cnt) {
       if (p.second > maxCount) {
           maxCount = p.second;
           ans = p.first;
       }
   }

   cout << ans << endl;

   return 0;
}


四、两种方法对比

从实现方式来看:

  • 第一种方法:通过比较来统计

  • 第二种方法:通过记录来统计

从效率来看:

  • 第一种方法:O(n²)

  • 第二种方法:O(n log n)

从代码结构来看:

  • 第一种方法:两层循环,逻辑分散

  • 第二种方法:结构清晰,统计集中


五、本质区别

关键差别不在语法,而在思维方式:

第一种方法是“比较思维”
第二种方法是“统计思维”

当问题从“一个个比”转变为“直接记录”,代码就会变得更简单、更高效。


六、学习建议

这类题目的正确学习路径是:

先用最基础的方法写出来,理解问题本质
再引入 map,优化统计过程
最后再接触更高效的结构(如 unordered_map

如果一开始就直接使用高级工具,很容易“会写但不理解”。


七、总结

这道题的价值不在于难度,而在于思维升级:

从重复比较,到一次统计
从手动计数,到自动管理

当能够自然想到用“记录”代替“比较”,说明已经迈入更高一层的编程理解。


野牛程序员教少儿编程与信息学奥赛-微信|电话:15892516892
野牛程序员教少儿编程与信息学竞赛-微信|电话:15892516892
  • 野牛程序员
  • 最新推荐

    热门点击