Week15 字符串哈希(HDU - 1880)

题目概述

ZJM 为了准备霍格沃兹的期末考试,决心背魔咒词典,一举拿下咒语翻译题
题库格式:[魔咒] 对应功能
背完题库后,ZJM 开始刷题,现共有 N 道题,每道题给出一个字符串,可能是 [魔咒],也可能是对应功能
ZJM 需要识别这个题目给出的是 [魔咒] 还是对应功能,并写出转换的结果,如果在魔咒词典里找不到,输出 “what?”

输入样例

首先列出魔咒词典中不超过100000条不同的咒语,每条格式为:

[ 魔 咒 ] 对 应 功 能 [魔咒] 对应功能 []

其中“魔咒”和“对应功能”分别为长度不超过20和80的字符串,字符串中保证不包含字符“[”和“]”,且“]”和后面的字符串之间有且仅有一个空格。魔咒词典最后一行以“@END@”结束,这一行不属于词典中的词条。
词典之后的一行包含正整数N(<=1000),随后是N个测试用例。每个测试用例占一行,或者给出“[魔咒]”,或者给出“对应功能”

输入样例:

[expelliarmus] the disarming charm
[rictusempra] send a jet of silver light to hit the enemy
[tarantallegra] control the movement of one's legs
[serpensortia] shoot a snake out of the end of one's wand
[lumos] light the wand
[obliviate] the memory charm
[expecto patronum] send a Patronus to the dementors
[accio] the summoning charm
@END@
4
[lumos]
the summoning charm
[arha]
take me to the sky

输出样例

每个测试用例的输出占一行,输出魔咒对应的功能,或者功能对应的魔咒。如果在词典中查不到,就输出“what?”

输出样例:

light the wand
accio
what?
what?

思路概述

一道非常经典的字符串哈希问题。

字符串哈希:
字符串哈希是一种利用哈希算法的字符串压缩方式,能够将字符串压缩为一个long long型的数字,在不出现哈希碰撞的情况下,使用哈希算法能够很好地完成字符串的识别问题。但由于在识别字符串时使用的哈希值必须相等,所以哈希算法只能用来计算字符串的完全匹配问题。
本题目中使用的哈希是BKDR哈希算法:
如果一个字符串长度为n,且其 i 位置上的字符为 s[i] ,则BKDR哈希算法如下:
H A S H = ( a [ 1 ] ∗ s e e d n + a [ 2 ] ∗ s e e d n − 1 + . . . + a [ n ] ∗ s e e d 1 ) HASH=(a[1]*seed^n+a[2]*seed^{n-1}+...+a[n]*seed^1) HASH=(a[1]seedn+a[2]seedn1+...+a[n]seed1)
这里需要注意的是,如果题目该定了MOD的值,需要在计算时除余MOD,如果没有给定一般选择1E9+7。公式中的seed是一些质数,一般取7、17、131.
注意:在使用字符串哈希匹配时,可能会出现错误,一旦两个不同的字符串使用哈希算法计算后得到了相同的哈希值,就会出现错误,一般把这种错误成为字符串哈希碰撞。比较好的解决方案是更换seed重新运行算法。

题解:
本题目求解的是[咒语–解释]的哈希匹配。将数据保存完成后,给定一个咒语,使用哈希算法算出哈希值并找到相应的解释的index,查表输出;给定一个解释,使用哈希算法算出哈希值并找到相应的咒语的index,查表输出。为了方便对应,为每一个字符串组(咒语+解释)分配一个共同的index,咒语和解释的哈希值唯一对应index。为了实现这种数据结构,使用map存放(HASH,index)的对应关系即可。

实现源码

#include
#include
#include
#include
#include
#include

using namespace std;

const long long mod = 1e9 + 7;
const int seed = 17;
const int M = 1e5 + 5;

map<long long, int> m1;//咒语 
map<long long, int> m2;//解释 
char r1[M][25];//咒语 
char r2[M][85];//解释

int index = 0;
long long hash1 = 0;
long long hash2 = 0;
long long quick_pow(int a, int x)
{
	long long res = 1;
	long long p = a;
	while (x)
	{
		if (x & 1) res = (res * p) % mod;
		p = (p * p) % mod;
		x >>= 1;
	}
	return res;
}
long long make_hash(string s)
{
	long long hash_res = 0;
	for (int i = 0; i < s.size(); i++)
		hash_res += (long long)s[i] * quick_pow(seed, s.size() - i);
	return hash_res;
}
void insert(string s)
{
	string s1, s2;
	for (int i = 1; i < s.size(); i++)
	{
		if (s[i] == ']')
		{
			s1 = s2;
			s2.clear();
			i++;
			continue;
		}
		s2 += s[i];
	}
	hash1 = make_hash(s1);
	hash2 = make_hash(s2);
	strcpy(r1[index], s1.c_str());
	strcpy(r2[index], s2.c_str());
	m1[hash1] = index;
	m2[hash2] = index;
}
void find_ans(string s)
{
	if (s[0] == '[')//用咒语找结果
	{
		string s_new;
		s_new.clear();
		for (int i = 1; i < s.size() - 1; i++)
			s_new += s[i];
		long long hash_num = make_hash(s_new);
		if (m1.count(hash_num) > 0)//找到了
		{
			int res = m1[hash_num];
			printf("%s\n", r2[res]);
		}
		else//没找到
		{
			printf("what?\n");
		}
	}
	else//用翻译找结果
	{
		long long hash_num = make_hash(s);
		if (m2.count(hash_num) > 0)//找到了
		{
			int res = m2[hash_num];
			printf("%s\n", r1[res]);
		}
		else//没找到
		{
			printf("what?\n");
		}
	}
}
int datagroup;
int main()
{
	index = 0;
	datagroup = 0;
	m1.clear();
	m2.clear();

	string s;
	getline(cin, s);
	while (s != "@END@")
	{
		insert(s);
		index++;
		getline(cin, s);
	}
	scanf("%d", &datagroup);
	getchar();
	for (int i = 0; i < datagroup; i++)
	{
		getline(cin, s);
		find_ans(s);
	}
	return 0;

}

你可能感兴趣的:(CSP精进之路,算法,字符串,哈希算法)