[模板]字符串哈希_自然溢出
一.可求子串哈希值的字符串哈希:
对一个字符串进行这样的哈希操作,得到数组hs,hs[i]表示该字符串1~i部分的子串的哈希值.
利用这个数组可以表示这个字符串任意子串对应的哈希值.
// (自然溢出) char s[1000]; // 将要进行操作的字符串 unsigned long long hs[1000], p[1000]; // 注意是unsigned long long, 这两个数组与字符串大小相同 scanf("%s", s + 1); p[0] = 1; int len = strlen(s + 1); for (int i = 1; i <= len; i++) { hs[i] = hs[i - 1] * 131 + s[i] - 'a' + 1; // hs[i] 即为字符串s在1~i部分的子串的哈希值,即前缀哈希值,如果有需要的话把这个循环倒着来一遍就可以得到后缀哈希值 p[i] = p[i - 1] * 131; // p[i] 表示131^i } /* 采用如下方法表示l到r的子串的哈希值: hs[r] - hs[l - 1] * p[r - l + 1]; 若两段字符串的哈希值相等,则视其为相同字符串.这使得比较两端字符串的复杂度为O(1) */
一般来说,我们取p=131或p=13331,此时(自然溢出)Hash产生冲突的概率极低,...
除了极特殊构造的数据上,上述Hash算法很难产生冲突,一般情况下上述Hash算法完全可以出现在题目的标准解答中.
----<<指南>>
P2957 [USACO09OCT]Barn Echoes G
重复部分只会出现在一个字符串的后缀和另一个字符串的前缀出现,那么只需要以O(n)枚举重复部分长度即可.注意有两种情况要考虑.
理论上说,用strcmp可以达到与比较哈希值相同的效果,只是前者实现起来要处理一些细节,并且复杂度最坏情况下达到了O(n).哈希值算法在O(n)预处理后每次比较只需要O(1)时间.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> using namespace std; int l1, l2, ans; char s1[100], s2[100]; unsigned long long hs1[100], hs2[100], p[100]; int main() { scanf("%s%s", s1 + 1, s2 + 1); l1 = strlen(s1 + 1), l2 = strlen(s2 + 1); p[0] = 1; for (int i = 1; i <= max(l1, l2); i++) { hs1[i] = hs1[i - 1] * 131 + s1[i] - 'a' + 1; hs2[i] = hs2[i - 1] * 131 + s2[i] - 'a' + 1; p[i] = 131 * p[i - 1]; } for (int i = 1; i <= l1; i++) if (hs1[l1] - hs1[l1 - i] * p[i] == hs2[i]) ans = i; for (int i = ans; i <= l2; i++) if (hs2[l2] - hs2[l2 - i] * p[i] == hs1[i]) ans = i; printf("%d\n", ans); return 0; }
二.全字符串哈希
有时候只需要判断两个独立的字符串是否相等,就不需要大费周章了.
char s[1510]; unsigned long long hs[SIZE]; // 可以存储SIZE个不同的字符串的哈希值 for (int i = 1; i <= n; i++) { scanf("%s", s);
/******计算字符串s的哈希值并存储到hs[i]中******/ for (int j = 0; s[j]; j++) { hs[i] *= 131; hs[i] += s[j]; // 相当于把字符用ASCII码转化为数字处理,也可以以特定的方式转化,有什么区别不好说 }
/*****************************************/ }
计算有多少个不同的字符串.把所有字符串转化为哈希值进行比较,由于哈希值是一个数字,就可以利用数字的特性比较:排序后逐个检查是否与上一个数字相等.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <cctype> #include <cstdio> #include <cstring> #include <iostream> using namespace std; int n, ans; char s[1510]; unsigned long long hs[10010]; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%s", s); for (int j = 0; s[j]; j++) { hs[i] *= 131; if (isalpha(s[j])) hs[i] += s[j] - 'a' + 10; else hs[i] += s[j] - '0'; } } sort(hs + 1, hs + 1 + n); for (int i = 1; i <= n; i++) if (hs[i] != hs[i - 1]) ans++; printf("%d\n", ans); return 0; }