【UKIEPC2015 C】【STL set map stringstream】Conversation Log 网络审查 被所有人都说过的话 map套set法+人哈希法

map套set法:

#include<stdio.h> 
#include<iostream>
#include<string.h>
#include<vector>
#include<sstream>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
const int N=1e4+5;
const int L=2e6+10;
set<string>name;//记录所有人名
map<string,set<string> >cnt;//记录说过每句话的所有人名
map<string,int>freq;//记录每句话被说过的总次数
map<string,int>::iterator it;
char nm[24];
char s[L];
char w[L];
int n,m;
vector<pair<int,string> >b;
int main()
{
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        //步骤一,读入这个人的名称
        scanf("%s",nm);
		name.insert(nm);

		//步骤二,用stringstream处理他说过的所有话
        gets(s);
        stringstream cinn(s);
        while(cinn>>w)//w是一句话
        {
            freq[w]++;//首先这句话的频率增加
            cnt[w].insert(nm);//然后记录所有说过这句话的人
        }
    }

	//步骤三,查询所有话,找到被所有人说过的话,按照(被说过的次数降序,字典序升序)构建pair并且排序输出
	n=name.size();
    for(it=freq.begin();it!=freq.end();it++)
    {
        if(cnt[it->first].size()==n)
        {
            b.push_back(make_pair(-it->second,it->first));
        }
    }
    sort(b.begin(),b.end());
    int g=b.size();
    if(g==0)puts("ALL CLEAR");
    else for(int i=0;i<g;i++)cout<<b[i].second<<endl;
    return 0;
}
/*
【trick&&吐槽】
1,读题一定要认真。脑补题意很有可能会出问题的
2,数组不能开小。人数最多1e4,话数可能达到1e6

【题意】
有m(1e4)	行信息,所有信息的总长度2e6
对于每行信息,
第一个单词是说话者的名字,长度不超过20。
接下来是这个说话者说过的所有单词。

我们要说出被所有说话者都说过的单词。
按照(单词使用频率,同频率下字典序)排列。
如果不存在这样的单词,输出ALL CLEAR

【类型】
STL

【分析】
首先,人名是有用的。因为被所有人都说过的单词,才可能是答案的备选单词。
我们怎么得到一个单词是否被所有人都使用过呢?

做法一:
对于每个单词,
	建立一个map<string,set<string> >,用set存这个单词被说过的人的名字
	建立一个map<string,int>,存这个单词被说过的次数
这样我们只要统计最后有多少个人说过话(记做n),判定其set.size()是否恰好为n,就能得到这个单词是否被所有人使用过。
	这里有一个可以优化的地方,就是先把单词名hash成一个int,然后对一个int再展开第二维度。

做法二:
对于每个单词,
	建立一个map<string,int>存说过这个单词的人的个数
	建立一个map<string,int>空间,存这个单词被说过的次数
这个与做法一不同的是,我们要如何维护说过这个单词的人的个数?
可以以人为关键点。存每个人所说过的单词。对于一个人第一次说某个单词,那么说这个单词的人的个数+1。

附上官方题解:
https://docs.google.com/presentation/d/1V4UHi1pLUhZR32H06SlIMi9egm9acbQe-DNCLpAQmf8/present?slide=id.gc6f83aa91_0_56
【时间复杂度&&优化】
复杂度其实也就是nlog(n)级别的。总的set/map size大小不会超过n,

【数据】
input
8
Jepson no no no no nobody never
Ashley why ever not
Marcus no not never nobody
Bazza no never know nobody
Hatty why no nobody
Hatty nobody never know why nobody
Jepson never no nobody
Ashley never never nobody no

output
no
nobody
never

input
2
Villain avast
Scoundrel ahoy

output
ALL CLEAR
*/

人哈希法:

#include<stdio.h> 
#include<iostream>
#include<string.h>
#include<vector>
#include<sstream>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
const int N=1e4+5;
const int L=2e6+10;
map<string,int>name;//给每个人的名字做编号
set<string>a[N];//记录每个人说过哪些话
map<string,int>cnt;//记录每句话被多少人说过
map<string,int>freq;//记录每句话被说过的总次数
map<string,int>::iterator it;
char nm[24];
char s[L];
char w[L];
int n,m;
vector<pair<int,string> >b;
int main()
{
    n=0;scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        //步骤一,映射得到这个人的编号
        scanf("%s",nm);
        int o;
        if(name.find(nm)==name.end())o=name[nm]=++n;
        else o=name[nm];

		//步骤二,用stringstream处理他说过的所有话
        gets(s);
        stringstream cinn(s);
        while(cinn>>w)//w是一句话
        {
            freq[w]++;//首先这句话的频率增加
            if(a[o].find(w)==a[o].end())//个人说如果之前没说过这句话
            {
                cnt[w]++;//这句话说的人数++
				a[o].insert(w);//并且insert这句话
            }
        }
    }

	//步骤三查询所有话,找到被所有人说过的话,按照(被说过的次数降序,字典序升序)构建pair并且排序输出
    for(it=cnt.begin();it!=cnt.end();it++)
    {
        if(it->second==n)
        {
            b.push_back(make_pair(-freq[it->first],it->first));
        }
    }
    sort(b.begin(),b.end());
    int g=b.size();
    if(g==0)puts("ALL CLEAR");
    else for(int i=0;i<g;i++)cout<<b[i].second<<endl;
    return 0;
}
/*
【trick&&吐槽】
1,读题一定要认真。脑补题意很有可能会出问题的
2,数组不能开小。人数最多1e4,话数可能达到1e6

【题意】
有m(1e4)	行信息,所有信息的总长度2e6
对于每行信息,
第一个单词是说话者的名字,长度不超过20。
接下来是这个说话者说过的所有单词。

我们要说出被所有说话者都说过的单词。
按照(单词使用频率,同频率下字典序)排列。
如果不存在这样的单词,输出ALL CLEAR

【类型】
STL

【分析】
首先,人名是有用的。因为被所有人都说过的单词,才可能是答案的备选单词。
我们怎么得到一个单词是否被所有人都使用过呢?

做法一:
对于每个单词,
	建立一个map<string,set<string> >,用set存这个单词被说过的人的名字
	建立一个map<string,int>,存这个单词被说过的次数
这样我们只要统计最后有多少个人说过话(记做n),判定其set.size()是否恰好为n,就能得到这个单词是否被所有人使用过。
	这里有一个可以优化的地方,就是先把单词名hash成一个int,然后对一个int再展开第二维度。

做法二:
对于每个单词,
	建立一个map<string,int>存说过这个单词的人的个数
	建立一个map<string,int>空间,存这个单词被说过的次数
这个与做法一不同的是,我们要如何维护说过这个单词的人的个数?
可以以人为关键点。存每个人所说过的单词。对于一个人第一次说某个单词,那么说这个单词的人的个数+1。

附上官方题解:
https://docs.google.com/presentation/d/1V4UHi1pLUhZR32H06SlIMi9egm9acbQe-DNCLpAQmf8/present?slide=id.gc6f83aa91_0_56
【时间复杂度&&优化】
复杂度其实也就是nlog(n)级别的。总的set/map size大小不会超过n,

【数据】
8
Jepson no no no no nobody never
Ashley why ever not
Marcus no not never nobody
Bazza no never know nobody
Hatty why no nobody
Hatty nobody never know why nobody
Jepson never no nobody
Ashley never never nobody no

8
Jepson no no no no nobody never
Ashley why ever not
Marcus no not never nobody
Bazza no never know nobody nobody nobody nobody nobody nobody
Hatty why no nobody
Hatty nobody never know why nobody
Jepson never no nobody
Ashley never never nobody no

2
Villain avast
Scoundrel ahoy

*/



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值