字符串哈希(Hash)
基础概念
作用: 快速判断两个串是否相等,判断一个字符串是否出现过。
更多应用可以看这篇好文。
字符串哈希实质上就是把每个不同的字符串转成不同的整数。
可以发现,与一个string有关的HASH值不仅仅跟每个字符的个数有关,还和字符所在的位数有关。
比如说对于字符串:abc,base设为32
也就是说: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; }
本文作者:kingwzun
本文链接:https://www.cnblogs.com/kingwz/p/16530119.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步