字符串哈希(Hash)
基础概念
作用: 快速判断两个串是否相等,判断一个字符串是否出现过。
更多应用可以看这篇好文。
字符串哈希实质上就是把每个不同的字符串转成不同的整数。
可以发现,与一个string有关的HASH值不仅仅跟每个字符的个数有关,还和字符所在的位数有关。
比如说对于字符串:abc,base设为32
\(a=hash[1]=a-'a'+1=1\)
\(ab=hash[2]=hash[1]∗32+b-'a'+1=32+2=34\)
\(abc=hash[3]=hash[2]∗32+c-'a'+1=1088+3=1091\)
也就是说:abc,可以映射为整数1091
也就是把字符串看成32进制的一个数。
通过简单的思考,即可知道转化公式为:
hash[i]=hash[i−1]∗base+idx(s[i]);
注意:
-
为了尽量避免冲突,base建议取:
131/1331
-
为了方便将变量定义为unsigned long long。
当存储的数据大于unsigned long long的存储范围时,会自动mod 264−1,就不用mod其他质数来保证唯一性了。
138. 兔子与兔子
模板题
题意:
每次选择两个区间,询问如果用两个区间里的 DNA 序列分别生产出来两只兔子,这两个兔子 DNA 序列是否一模一样。
代码:
#include <bits/stdc++.h>
#define ins 0x3f3f3f3f
#define pii pair<int, int>
#define ull unsigned long long
using namespace std;
const int N = 1000001,base=131;
ull h[N],p[N];
char str[N];
ull get(int l,int r){
return h[r]-h[l-1]*p[r-l+1];
}
void build(){
int n=strlen(str+1);
p[0]=1;
for(int i=1;i<=n;i++){
h[i]=h[i-1]*base+str[i]-'a'+1;
p[i]=p[i-1]*base;
}
}
void solve()
{
cin>>str+1;
build();
int n;
cin >> n;
int l1,r1,l2,r2;
for(int i=0;i<n;i++){
cin>>l1>>r1>>l2>>r2;
if(get(l1,r1)==get(l2,r2)) puts("Yes");
else puts("No");
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}
双哈希
思想:
按照不同的模数分别进项哈希,然后分别比较。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
const ull mod1 = 1e9 + 7, mod2 = 2147483647;
const int N = 4e5 + 10;
const int p = 131;
ull a1[N], a2[N], h1[N], h2[N];
pair<ull, ull> res[N];
map<pair<ull, ull>, ull> mp;
void hash1(string s)
{ //预处理hash函数前缀和
a1[0] = 1;
int n = s.size();
for (int i = 1; i <= n; i++)
{
a1[i] = a1[i - 1] * p % mod1;
h1[i] = (h1[i - 1] * p % mod1 + (s[i - 1] - 'a' + 1)) % mod1;
}
}
void hash2(string s)
{ //预处理hash函数前缀和
a2[0] = 1;
int n = s.size();
for (int i = 1; i <= n; i++)
{
a2[i] = a2[i - 1] * p % mod2;
h2[i] = (h2[i - 1] * p % mod2 + (s[i - 1] - 'a' + 1)) % mod2;
}
}
ull get1(int l, int r)
{ //计算s[l--r]的hash值
return (h1[r] - h1[l - 1] * a1[r - l + 1] % mod1 + mod1) % mod1;
}
ull get2(int l, int r)
{ //计算s[l--r]的hash值
return (h2[r] - h2[l - 1] * a2[r - l + 1] % mod2 + mod2) % mod2;
}
int main()
{
int n;
cin >> n;
string s;
int pos = 0;
while (n--)
{
cin >> s;
int len = s.size();
hash1(s);
hash2(s);
mp[{h1[len], h2[len]}]++;
for (int i = 1; i + i < len; i++)
{ //寻找字符串s中前部分和后部分相同的子串
if (get1(1, i) == get1(len - i + 1, len) && get2(1, i) == get2(len - i + 1, len))
{
res[pos++] = {get1(i + 1, len - i), get2(i + 1, len - i)};
}
}
}
ll ans = 0;
for (int i = 0; i < pos; i++)
{ //统计与子串相同的字符串
ans += mp[res[i]];
}
for (auto it : mp)
{ //统计相同字符串
ans += it.second * (it.second - 1) / 2;
}
cout << ans << endl;
return 0;
}