Hash的用处就是将一个字符串从头到尾的特殊化记录一下,每次要判断字符串是否相等,直接看Hash值是否相等就好了
这里用的均为BKDR-Hash
POJ 2774
题目链接:http://poj.org/problem?id=2774
题目大意:给你两个字符串,求它们的最长公共子串的长度
思路:对两个字符串都Hash扫一下, 通过二分来判定公共子串长度,先求s1中长度为t的子串的Hash值,再求s2中的,对比是否相等
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn = 100005;
char s1[maxn], s2[maxn];
ull hash1[maxn], hash2[maxn], a[maxn], pows[maxn];
int len1, len2;
inline ull getHash(ull h[], int i, int j){ //计算[i, j)子串的hash值
return h[j] - h[i] * pows[j-i];
}
bool solve(int t) //判断是否有长度为t的解
{
int n = 0;
for(int i = t; i <= len1; i++){
a[n++] = getHash(hash1, i-t, i); //计算s1的每一个长度为t的子串的hash值
}
sort(a, a+n);
for(int i = t; i <= len2; i++){
ull h = getHash(hash2, i-t, i); //计算s2的每一个长度为t的子串的hash值
if(binary_search(a, a+n, h)) return true; //查找s1中是否有子串hash值与其相等
}
return false;
}
int main()
{
cin >> s1 >> s2;
len1 = strlen(s1), len2 = strlen(s2);
int l = 0, r = max(len1, len2);
pows[0] = 1;
for(int i = 1; i <= r; i++){
pows[i] = pows[i-1]*131; //预处理幂次
}
hash1[0] = 0;
for(int i = 0; i < len1; i++){
hash1[i+1] = hash1[i]*131 + s1[i]; // 预处理s1的前缀hash值
}
for(int i = 0; i < len2; i++){
hash2[i+1] = hash2[i]*131 + s2[i];
}
while(l != r){
int t = (l+r) >> 1;
if(solve(t+1)) l = t+1;
else r = t;
}
cout << l << '\n';
return 0;
}
HDU 4821
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4821
题目大意:一个字符串,它的子串有m个长为l的小子串构成,这些小子串各不相同,问有多少个子串?
思路:Hash之后,从头扫+移块扫,对每个长为m*l的子串内部进行判断,可以用map来进行存储每一个小子串的Hash值,方便计数
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn = 1e5+5;
char s[maxn];
ull Hash[maxn], base[maxn];
map<ull, int> p;
int main()
{
int m, l;
while(~scanf("%d %d", &m, &l)){
cin >> s;
int len = strlen(s);
base[0] = 1;
for(int i = 1; i <= len; i++){
base[i] = base[i-1]*131;
}
Hash[0] = 0;
for(int i = 0; i < len; i++){
Hash[i+1] = Hash[i]*131 + s[i];
}
int a = 0;
for(int i = 0; i < l && i+m*l <= len; i++){
p.clear();
for(int j = i; j < m*l+i; j+=l){
p[Hash[j+l] - Hash[j]*base[l]]++;
}
if(p.size() == m) a++;
for(int j = i; j+l <= len-m*l; j+=l){ //移块扫
p[Hash[j+l] - Hash[j]*base[l]]--;
if(p[Hash[j+l] - Hash[j]*base[l]] == 0) p.erase(Hash[j+l] - Hash[j]*base[l]); // 将原先块区删除
p[Hash[j+m*l+l] - Hash[j+m*l]*base[l]]++; // 添加新的块区
if(p.size() == m) a++;
}
}
cout << a << '\n';
}
return 0;
}