哈希表(拉链法和开放寻址法)与字符串哈希

拉链法哈希表

#include <iostream>

using namespace std;


/*

维护一个集合,支持如下几种操作:

1." I x ",插入一个数 x ;
2." Q x ",询问数 x 是否在集合中出现过;
现在要进行 N 次操作,对于每个询问操作输出对应的结果

输入格式
第一行包含整数 N ,表示操作数量。
接下来 N 行,每行包含一个操作指令,操作指令为" I x "," Q x "中的一种。

输出格式
对于每个询问指令" Q x ",输出一个询问结果,如果 x 在集合中出现过,则输出" Yes ",否则输出" No "。
每个结果占一行。

数据范围
1 ≤ N ≤ 10^5
-10^9 ≤ x ≤ 10^9


*/







const int N = 100003; // 使用质数,使发生哈希冲突的几率较低

int hs[N], val[N], ne[N], idx;


// 哈希函数,返回哈希值
int hash_func(int x)
{
	return (x % N + N) % N; // 负数的模为负数,此操作把负数转为正数
}


// 在哈希表中插入数值x
void insert(int x)
{
	int k = hash_func(x); // k为哈希值

	// 单链表插入操作
	val[idx] = x;
	ne[idx] = hs[k];
	hs[k] = idx;
	idx++;
}


// 查询哈希表中是否存在数值x
bool find(int x)
{
	int k = hash_func(x);

	for (int i = hs[k]; i != -1; i = ne[i])
	{
		if (val[i] == x)
		{
			return true;
		}
	}

	return false;
}

int main()
{
	// 初始化哈希表,-1结尾表示没有存储值
	memset(hs, -1, sizeof hs);

	int n;
	cin >> n;

	while (n--)
	{
		char op;
		int x;
		cin >> op >> x;

		if (op == 'I')
		{
			insert(x);
		}
		else if (op == 'Q')
		{
			find(x);
		}
	}

	return 0;
}

开放寻址法哈希表

#include <iostream>

using namespace std;

// 开辟数组的大小一般要题目数值范围的2-3倍,且为质数
const int N = 200003;
// 哈希表中存储的为null表示该位置没有被使用
const int null = 0x3f3f3f3f;


int hs[N];



// 哈希函数
int hash_func(int x)
{
	return (x % N + N) % N;
}


// 如果 x 存在,返回x所在位置;如果不存在,返回 x 可以存储的位置
int find(int x)
{
	int k = hash_func(x);

	// 不考虑删除操作
	while (hs[k] != null && hs[k] != x)
	{
		k++;

		// 从表头开始查询可插入位置
		// 不考虑哈希表已填满的情况(如果填满且没有x值,将进入死循环)
		if (k == N)
		{
			k = 0;
		}
	}

	// 判断的时候判断 hs[k] 是否等于 null值,即可判断是否存在x
	return k;
}


int main()
{
	// 初始化,每个字节都存储 0x3f,则每个 int 都存储0x3f3f3f3f
	memset(hs, 0x3f, sizeof hs);

	int n;
	cin >> n;

	while (n--)
	{
		char op;
		int x;

		cin >> op >> x;

		int k = find(x);

		if (op == 'I')
		{
			hs[k] = x;
		}
		else if (op == 'Q')
		{
			if (hs[k] == null)
			{
				cout << "No" << endl;
			}
			else
			{
				cout << "Yes" << endl;
			}
		}
			
	}


	return 0;
}

字符串哈希

#include <iostream>
using namespace std;


/*

给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数l1,r1,l2,r2,请你判断 [l1,r1] 和 [l2,r2]这两个区间所包含的字符串子串是否完全相同。
字符串中只包含大小写英文字母和数字。

输入格式
第一行包含整数 n 和 m ,表示字符串长度和询问次数。
第二行包含一个长度为 n 的字符串,字符串中只包含大小写英文字母和数字。
接下来 m 行,每行包含四个整数 l1, r1, l2, r2,表示一次询问所涉及的两个区间。
注意,字符串的位置从1开始编号。

输出格式
对于每个询问输出一个结果,如果两个字符串子串完全相同则输出" Yes ",否则输出" No "。
每个结果占一行。

*/





typedef unsigned long long ULL;

const int N = 100010;
const int P = 131; // 表示131进制,还可以取13331,取这两个值可降低哈希冲突的概率

int n, m;
char str[N];
ULL hs[N], p[N]; // p[i]表示 P 的 i 次方为多少,预处理后方便后面处理 区间字符串哈希值
				 // 字符串最长长度为N,其哈希值会十分庞大,需要将其映射到比较小的数值范围 [0,Q), 即求出的哈希值 模(%) 上 Q(Q 一般取 2^64), 所以定义 unsigned long long类型的哈希数组即可省去 取模(%) 的步骤,溢出即是取模



// 返回 [l,r] 区间字符串的哈希值
ULL get_hs(int l, int r)
{
	// 区间字符串哈希值公式
	return hs[r] - hs[l - 1] * p[r - l + 1];
}


int main()
{
	// 从下标为 1 开始存储
	cin >> n >> m >> (str + 1);

	p[0] = 1; // P的0次方为1

	for (int i = 1; i <= n; i++)
	{
		p[i] = p[i - 1] * P;
		
		// 计算 [1,i] 字符串的哈希值
		hs[i] = hs[i - 1] * P + str[i];
	}


	// 处理询问
	while (m--)
	{
		int l1, r1, l2, r2;
		cin >> l1 >> r1 >> l2 >> r2;

		// 字符串哈希基本可以忽视哈希冲突的概率,因此哈希值相同即字符串相同
		if (get_hs(l1, r1) == get_hs(l2, r2))
		{
			cout << "Yes" << endl;
		}
		else
		{
			cout << "No" << endl;
		}
	}



	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值