字符串哈希

https://www.luogu.com.cn/blog/pks-LOVING/zi-fu-chuan-xue-xi-bi-ji-ha-xi-hash-yu-zi-dian-shu-trie
这一篇好棒!!!
转自 http://www.yhzq-blog.cc/字符串hash总结/
这一篇也好棒!!!

据我的理解,Hash就是一个像函数一样的东西,你放进去一个值,它给你输出来一个值。输出的值就是Hash值。

那字符串Hash就非常好理解了。就是把字符串转换成一个整数的函数。而且要尽量做到使字符串对应唯一的Hash值。

在信息学竞赛中只会用到一种名为“BKDR Hash”的字符串Hash算法。

它的主要思路是选取恰当的进制,可以把字符串中的字符看成一个大数字中的每一位数字,不过比较字符串和比较大数字的复杂度并没有什么区别(高精数的比较也是 \(O(n)\) 的),但只要把它对一个数取模,然后认为取模后的结果相等原数就相等,那么就可以在一定的错误率的基础上\(O(1)\)进行判断了。

那么我们选择什么进制比较好?

首先不要把任意字符对应到数字0,比如假如把a对应到数字0,那么将不能只从Hash结果上区分ab和b(虽然可以额外判断字符串长度,但不把任意字符对应到数字0更加省事且没有任何副作用),一般而言,把a-z对应到数字1-26比较合适。

关于进制的选择实际上非常自由,大于所有字符对应的数字的最大值,不要含有模数的质因子(那还模什么),比如一个字符集是a到z的题目,选择27、233、19260817 都是可以的。

模数的选择(尽量还是要选择质数):

绝大多数情况下,不要选择一个109级别的数,因为这样随机数据都会有Hash冲突。
最稳妥的办法是选择两个\(10^9\)级别的质数,只有模这两个数都相等才判断相等,但常数略大,代码相对难写,目前暂时没有办法卡掉这种写法(除了卡时间让它超时)。

自然溢出

/*
Date:2022.9.28
Source:
knowledge:
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define orz cout << "AK IOI" <<"\n"
#define ull unsigned long long

using namespace std;
const int maxn = 1e4 + 10;
const int base = 131;

int read()
{
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void print(int X)
{
	if(X < 0) X = ~(X - 1), putchar('-');
	if(X > 9) print(X / 10);
	putchar(X % 10 ^ '0');
}
int Max(int a, int b){
	return a > b ? a : b;
}
int Min(int a, int b){
	return a < b ? a : b;
}
int n, ans = 1; 
ull a[maxn];
char s[1510];
ull hashh(char s[])
{
	int len = strlen(s);
	ull ans = 0; 
	for(int i = 1; i < len; i++)
		ans = ans * base + (ull)s[i];
	return ans;
}
int main()
{
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
	n = read();
	for(int i = 1; i <= n; i++)
	{
		scanf("%s", s);
		a[i] = hashh(s);	
	}
	sort(a + 1, a + n + 1);
	for(int i = 2; i <= n; i++) if(a[i] != a[i - 1]) ans++;
	print(ans);
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

双哈希

/*
Date:2022.9.28
Source:
knowledge:
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define orz cout << "AK IOI" <<"\n"

using namespace std;
const int base = 131;
const int maxn = 10010;
int mod1 = 19260817;
int mod2 = 19660813;

int read()
{
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void print(int X)
{
	if(X < 0) X = ~(X - 1), putchar('-');
	if(X > 9) print(X / 10);
	putchar(X % 10 ^ '0');
}
int Max(int a, int b){
	return a > b ? a : b;
}
int Min(int a, int b){
	return a < b ? a : b;
}
int n, ans = 1;
char s[1510];
struct node{
	int x, y;
}a[maxn];
int hash1(char s[])
{
	int len = strlen(s), ans = 0;
	for(int i = 0; i < len; i++)
		ans = (ans * base % mod1 + s[i]) % mod1; 
	return ans;
}
int hash2(char s[])
{
	int len = strlen(s), ans = 0;
	for(int i = 0; i < len; i++)
		ans = (ans * base % mod2 + s[i]) % mod2; 
	return ans;
}
bool cmp(node a, node b)
{
	return a.x < b.x; 
}
int main()
{
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
	n = read();
	for(int i = 1; i <= n; i++)
	{
		scanf("%s", s); 
		a[i].x = hash1(s);
		a[i].y = hash2(s);
	}
	sort(a + 1, a + n + 1, cmp);
	for(int i = 2; i <= n; i++)
	{
		if((a[i].x != a[i - 1].x) || (a[i].y != a[i - 1].y)) ans++;	
	}
	print(ans);
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}


posted @ 2020-12-30 16:10  _程门立雪  阅读(121)  评论(0编辑  收藏  举报