洛谷P3879 [TJOI2010] 阅读理解 哈希Hash解法

题目描述:

英语老师留了 N 篇阅读理解作业,但是每篇英文短文都有很多生词需要查字典,为了节约时间,现在要做个统计,算一算某些生词都在哪几篇短文中出现过。

输入格式

第一行为整数 N ,表示短文篇数,其中每篇短文只含空格和小写字母。

按下来的 N 行,每行描述一篇短文。每行的开头是一个整数 L ,表示这篇短文由 L 个单词组成。接下来是 L 个单词,单词之间用一个空格分隔。

然后为一个整数 M ,表示要做几次询问。后面有 M 行,每行表示一个要统计的生词。

输出格式

对于每个生词输出一行,统计其在哪几篇短文中出现过,并按从小到大输出短文的序号,序号不应有重复,序号之间用一个空格隔开(注意第一个序号的前面和最后一个序号的后面不应有空格)。如果该单词一直没出现过,则输出一个空行。

输入输出样例

输入 #1

3 9 you are a good boy ha ha o yeah 13 o my god you like bleach naruto
one piece and so do i 11 but i do not think you will get all the
points 5 you i o all naruto

输出 #1

1 2 3

2 3

1 2

3

2

说明/提示

对于 30% 的数据, 1 ≤ M ≤ 10^3。

对于 100%100%100% 的数据,1 ≤ M ≤ 10^4
1 ≤ N ≤ 10^3 。

每篇短文长度(含相邻单词之间的空格)≤ 5×10^3字符,每个单词长度 ≤ 20 字符。

每个测试点时限 2 秒。

---------------------------分割线-------------------------------

第一反应是用桶排,如果能把单词表示成一个编码,直接用int数组就可以做了,但是显然,20个字母组成的单词编码的数据会很大。

但我可以想到可以退而求其次:对字母的前两个字母进行编码,编码相同的单词存储到同一个map< string , list>中,string记录单词,list记录单词在哪几个文章中出现。这样的由于前两个字母都一样的单词很少,查找几乎是线性时间就能结束。

建立一个这样的数组存储所有的单词。
maparticle[27][27];
编码使用如下规则
编 码 = 字 符 A S C I I 码 − ′ a ′ + 1 编码 = 字符ASCII码 - 'a' + 1 =ASCIIa+1
如何存储呢?假如是单词是“abc”,‘a’ - ‘a’ + 1 = 1;‘b’ - ‘a’ + 1 = 2把它存在article[1][2]里。
特别的,仅有一个字母的单词,比如"a" 存储在article[1][0]中。

查找单词时按照同样的规则。

AC代码:

#include
#include
#include
#include
using namespace std;

map<string,list<int> >article[27][27];

string key;//待查询的单词

void Read(int n);//输入第n篇文章
void Solve();
int main()
{
     
	int n, m;
	cin >> n;
	for(int i = 1; i <= n; ++i)
	Read(i);

	cin >> m;
	for(int i = 0; i < m; ++i)
	{
     
		cin >> key;
		Solve();
	}
	return 0;
}

void Read(int n)
{
     
	int l;
	cin >> l;
	list<int>tmp;
	tmp.push_back(n);
	for(int i = 0; i < l; ++i)
	{
     
		string w;
		cin >> w;
		int c = w[0] - 'a' + 1;
		int d = 0;
		if(w.size() > 1)
		d = w[1] - 'a' + 1;//进行编码
		pair<map<string,list<int> >::iterator,bool> flag = article[c][d].insert(make_pair(w,tmp));
		if(!flag.second)//如果插入失败
			flag.first->second.push_back(n);
	}
}
void Solve()
{
     
	int a = key[0] - 'a' + 1;
	int b = 0;
	if(key.size() > 1)
		b = key[1] - 'a' + 1;
	map<string,list<int> >::iterator flag = article[a][b].find(key);
	if(flag != article[a][b].end())
	{
     
		list<int> *itr = &(flag->second);
		int tmp = itr->front();
		cout << tmp ;
		for(list<int>::iterator i = itr->begin(); i != itr->end(); ++i)
		{
     
		
			if(*i != tmp)
			{
     
				cout << " " << *i;
			}//注意list可能会插入多个相同的数值,所以需要与前一个数字比较判断
			tmp = *i;
		}
	}
	cout << endl;
}

你可能感兴趣的:(题解,C++,算法,IOI,洛谷,C++)