哈希表之账号密码查询

本文通过两种方法利用哈希表实现账号密码查询。方法一采用字符权重结合开放寻址法,通过限制计算位数和调整权重解决冲突。方法二使用字符相加配合拉链法,利用动态链表解决冲突。对于名字只含大写字母的情况,这两种方法都能有效处理数据并检查用户登录信息。
摘要由CSDN通过智能技术生成

题目概述

请用哈希表存储客户的账户和密码信息,然后检查用户输入的账户和密码是否正确。

输入:第一行是一个正整数n(<=100,000),表示客户数目,然后是每个客户的信息。
每个客户信息的第一行是账号,第二行是密码。

账户信息之后是用户登录信息。第一行是登录用户数目m(<=10,000), 然后是m个用户输入的账户(第一行)和密码(第二行)。假设用户名只含大写字母。

输出:检查每个用户(共m个登录用户)的登录信息,如果正确,则输出账户及“OK”字样,否则输出账户及“Invalid user name or password”字样。

要求:
时间限制: 3000 ms
空间限制: 32 MB
(原题还增加了密码md5加密,由于只是简单地调用函数,故此处省略)

方法一 字符权重 + 开放寻址法

哈希函数的选用—字符权重

由于名字只含大写字母,很直接地想到将A~Z对应成26进制,然后计算不同字符串的哈希值。但是这样写,当名字越长,哈希出来的值越来越大。
一位:26
二位:702
三位:18278
四位:475254
五位:12356630
在第四位时,就已经超过了我们要存放的数据大小,很容易超过限定内存。

int myhash(string s) {
	int len = s.size();
	int id = 0;
	int index = 1;
	for (int i = 0; i < len; i++) {
		id += index *(s[i] - 'A' + 1);
		index *= 26;
	}
	return id;
} 

但是我们也能稍微改进一下,一是限定计算的位数,二是更改不同位置的权重~~(说白了就是卡时间和卡空间的暴力做法qwq)~~

当限制计算前5位数,每位权重为10^i,此时下标最高为288886,不会超出太多(以下为更改后)

int myhash(string s) {
	int len = s.size();
	int id = 0;
	int index = 1;
	for (int i = 0; i < len && i < 5; i++) {
		id += index *(s[i] - 'A' + 1);
		index *= 10;
	}
	return id;
} 

冲突的设计—开放寻址法

像这种数据分散很开,并且最高下标远高于实际需要存储的散列表,在发生冲突时,可以考虑使用开放寻址法,使空余空间得到充分利用。
为了方便( ),这里直接使用 线性探测

当存放数据时,发现位置已被占用,则从该位置开始,往后一个个挪去探测是否有空位。

完整代码如下(还需增加上面的自定义的哈希函数)

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

string name[288886];
string passw[288886];
int check[288886] = {0};
//判断用户名是否存在,或者密码是否正确
bool checkpass(string each_name, string password, int id) {
	while (check[id]) {
		if (name[id] == each_name){
			if (password == passw[id]) return true;
			return false;
		}
		id++;
	    if (id == 288886) id = 0;
	}
	return false;
}

int main() {
	int user;
	int test;
	int id;
	cin >> user;
	string each_name;
	string password;
	//录入的用户数
	for (int i = 0; i < user; i++) {
		cin >> each_name;
		cin >> password;
		
		int id = myhash(each_name);
		
		while (check[id]) {
			id++;
			if (id == 288886) id = 0;
		}
		check[id] = 1;
		name[id] = each_name;
		passw[id] = password;
	} 
	//尝试登陆的测试数
	cin >> test;
	
	while (test--) {
		cin >> each_name;
		cin >> password;
		id = myhash(each_name);	

		if (checkpass(each_name, password, id) ) cout << each_name << ":OK" << endl;
		else cout << each_name << ":Invalid user name or password" << endl;	
	}		
	return 0;
}

方法二 字符相加 + 拉链法

哈希函数的选用—字符相加

还能想到一种方法是,将字符串中每个字母A ~ Z对应1 ~ 26,然后把各个位置的字符串值直接相加得到哈希值。在客户名字不长的情况下,下标500就绰绰有余了。
但是这种方法面临的问题是:发生冲突的可能性大大增加,要多考虑时间成本。

int myhash(string s) {
	int len = s.size();
	int id = 0;
	for (int i = 0; i < len; i++) {
		id +=  s[i] - 'A' + 1;
	}
	return id;
} 

冲突的设计—拉链法

而这种数据密集的散列表,更多使用 拉链法 解决冲突。
当发生冲突时,拉出一个动态链表代替静态顺序存储结构,可以避免哈希函数的冲突。我们也完全可以使用vector容器代替自己写链表,减少出错。

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

vector<bool> check(500,false);
vector<vector<string> > r_name(500);
vector<vector<string> > r_pass(500);

bool checkpass(string each_name,string password ,int id) {
	if (!check[id]) return false;
	
	int len = r_name[id].size();
	
	for (int i = 0; i < len; i++) {
		if (r_name[id][i] == each_name) {
			if (r_pass[id][i] == password)  return true;
			return false;
		}
	}
	return false;
} 

int main() {
	int user;
	int test;
	int id;
	string each_name;
	string password;
	//录入的用户数
	cin >> user;
	
	for (int i = 0; i < user; i++) {
		cin >> each_name;
		cin >> password;
		id = myhash(each_name);
		
		if (!check[id]) check[id] = true;
		r_name[id].push_back(each_name);
		r_pass[id].push_back(password);		
	}
	//尝试登陆的测试数
	cin >> test;
	
	while (test--) {
		cin >> each_name;
		cin >> password;
		id = myhash(each_name);	

		if (checkpass(each_name,password,id) ) cout << each_name << ":OK" << endl;
		else cout << each_name << ":Invalid user name or password" << endl;	
	}	
	return 0;
}

写得很烂的小菜鸡wwwwww
如有问题的地方,欢迎和我交流qwq

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值