字符串哈希
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;
}