码迷,mamicode.com
首页 > 其他好文 > 详细

7-1 词频统计 (30 分)

时间:2019-06-08 23:04:29      阅读:725      评论:0      收藏:0      [点我收藏+]

标签:类类型   修改   tchar   次数   form   ted   bre   UNC   上线   

词频统计

参考:
对参考的代码进行了一些补充和修改,大体思路没变
并不是想说这题多难,只是在这题可以用STL的很多结构帮助解题,所以能让代码变得很简单

题目

7-1 词频统计 (30 分)

请编写程序,对一段英文文本,统计其中所有不同单词的个数,以及词频最大的前10%的单词。
所谓“单词”,是指由不超过80个单词字符组成的连续字符串,但长度超过15的单词将只截取保留前15个单词字符。而合法的“单词字符”为大小写字母、数字和下划线,其它字符均认为是单词分隔符。
输入格式:
输入给出一段非空文本,最后以符号#结尾。输入保证存在至少10个不同的单词。
输出格式:
在第一行中输出文本中所有不同单词的个数。注意“单词”不区分英文大小写,例如“PAT”和“pat”被认为是同一个单词。
随后按照词频递减的顺序,按照词频:单词的格式输出词频最大的前10%的单词。若有并列,则按递增字典序输出。
输入样例:
This is a test.

The word "this" is the word with the highest frequency.

Longlonglonglongword should be cut off, so is considered as the same as longlonglonglonee. But this_8 is different than this, and this, and this...#
this line should be ignored.
输出样例:(注意:虽然单词the也出现了4次,但因为我们只要输出前10%(即23个单词中的前2个)单词,而按照字母序,the排第3位,所以不输出。)
23
5:this
4:is

思路

题目要求记录词频,那就记录每个词出现的次数
由于‘#’表示结束,那么就要逐字读入进行判断,但这要这么变成一个单词呢?只要把每个读到的字符接在字符串的后面就行了
然后可以构建一个列表,表中有每个单词及其出现次数

用到的结构及函数

begin()和end()函数

基本上线性存储类类型都会有的结构(vector,map),其实就是快速定位到第一个(通常下标是0)和最后一个数据。要注意的是他们可以直接进行加减运算
通常在初始化或者函数中用到

vector<pair<string ,int>> v(ma.begin(), ma.end());    //定义一个pair<string ,int>类型的数组,并且数组第一个数据为ma的第一个数据,最后一个数据为ma的最后一个数据
sort(v.begin(), v.end(), cmp);    //将v从v 的第一个到最后一个数据根据自定义的比较函数cmp进行分类。sort的自定义比较函数之后会说
//来自C++Reference的样例
int myints[] = {32,71,12,45,26,80,53,33};
  std::vector<int> myvector (myints, myints+8);               // 32 71 12 45 26 80 53 33 (对前八个数排序)

  std::sort (myvector.begin(), myvector.begin()+4);           //(12 32 45 71)26 80 53 33 (对前四个数排序)

pair(对组)

pair是C++定义的模板类型,可以同时存储两个类型的数据,其实可以用结构体实现的呢

pair<string, int> a    //定义
a.firts="Hello World";    //对第一个数据进行操作
a.second="3";             //对第二个数据进行操作

map

先来看看C++reference的定义

/*
Maps are associative containers that store elements formed by a combination of a key value and a mapped value, following a specific order.
In a map, the key values are generally used to sort and uniquely identify the elements, while the mapped values store the content associated to this key. 
The types of key and mapped value may differ, and are grouped together in member type value_type, which is a pair type combining both:
*/
 
typedef pair<const Key, T> value_type;

其实,我们可以把map理解为一个容器,其内部存的是一组键值对,即两个不同类型的数据。
可以理解为“关键字”以及“关键字的值”(没错,和pair很像)

vector(容器)

vector可以看成加强版数组,用于储存相同类型的多个数据。这里要介绍他的排序函数sort()
从C++reference中可以找到以下例子

 int myints[] = {32,71,12,45,26,80,53,33};
  std::vector<int> myvector (myints, myints+8);               //初始化为 32 71 12 45 26 80 53 33(myints的前八个放入vector)

  // using default comparison (operator <):
  std::sort (myvector.begin(), myvector.begin()+4);           //(12 32 45 71)26 80 53 33

  // using function as comp
  std::sort (myvector.begin()+4, myvector.end(), myfunction); // 12 32 45 71(26 33 53 80)

  // using object as comp
  std::sort (myvector.begin(), myvector.end(), myobject);     //(12 26 32 33 45 53 71 80)

可以看出sort()其实可以自己加函数进行排序,不过如果自己每加的话就默认非降序排列
那如果我要自己加函数要怎么加呢?
参考:【C++】从最简单的vector中sort用法到自定义比较函数comp后对结构体排序的sort算法
首先自定义比较函数的返回值是bool型的,这里给出一个例子

    bool comp(int a,int b){
        return a>b;
    }

    sort(v.begin(), v.end(), comp);

比较时sort函数根据comp函数进行判断输的大小,系统默认a<b时返回真,于是从小到大排,而我的comp函数设定为a>b时返回为真,那么最终得到的排序结果也相应的从小到大变成从大到小。其实可以这样理解:排序结束后,a是前面的数,b是后面的数,我们的自定义函数是为了定义a与b的关系
再让我们来看代码中的例子

bool cmp(pair<string, int> a, pair <string, int> b) {
    bool result = false;
    if (a.second == b.second&&a.first < b.first) {
        result = true;
    }
    else if (a.second > b.second) {
        result = true;
    }
    return result;
}

sort(v.begin(), v.end(), cmp);

那么上述式子就表明,对vector中的第一个数据到最后一个数据排列。
因为vector中的数据类型是pair,根据自定义的比较函数,当pair的second(词语出现次数)相等时,first(单词)小的在前面(若有并列,则按递增字典序输出)。

代码

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

using namespace std;

bool cmp(pair<string, int> a, pair <string, int> b);

int main() {
    char ch;
    string s;   //字符串用于记录一个单词
    map<string, int> ma;    //map记录词频,string代表的单词出现次数为int
    do {
        ch = getchar();
        //当读到的是合法字符(大小写字母、数字下划线)
        if ((ch >= 'a'&&ch <= 'z') || (ch >= 'A'&&ch <= 'Z') || (ch >= '0'&&ch <= '9') || ch == '_') {
            if (s.size() <= 14) {   //当长度为14时再进行一次接入,长度为15就停止接入
                if (ch >= 'A'&&ch <= 'Z') {     //把大写换成小写
                    ch += 32;
                }
                s += ch;    //把单个字符ch接到字符串s后,string中有运算符重载所以加法表示接在后面
            }
        }
        else {      //当不是合法字符就表示这个词读取结束了,出现次数+1
            if (s.size() > 0) {
                ma[s]++;
            }
            s.clear();      //清空字符串以统计下一个单词
        }
        if (ch == '#') {    //读到#退出循环
            break;
        }
    } while (ch != '#');
    vector<pair<string ,int>> v(ma.begin(), ma.end());        //存储pair的一个数组(把vector理解为增强版的数组)
    sort(v.begin(), v.end(), cmp);
    cout << v.size() << endl;
    int cnt = (int)(ma.size()*0.1);
    for (int i = 0; i < cnt; i++) {
        cout << v[i].second << ":" << v[i].first << endl;
    }
    return 0;
}

//利用pair数据,每个pair数据都含有一个string数值和int数值
//
bool cmp(pair<string, int> a, pair <string, int> b) {
    bool result = false;
    if (a.second == b.second&&a.first < b.first) {
        result = true;
    }
    else if (a.second > b.second) {
        result = true;
    }
    return result;
}

7-1 词频统计 (30 分)

标签:类类型   修改   tchar   次数   form   ted   bre   UNC   上线   

原文地址:https://www.cnblogs.com/luoyang0515/p/10991972.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!