字符串 h a s h hash hash 就是把一个字符串映射成一个非负整数。
(同时碰撞的概率极低)
设计一个较大质数 p p p,把字符串看作 p p p 进制数。(如 31 31 31, 131 131 131, 1331 1331 1331)
(想想为什么要用质数)
对于已知的字符串 s s s,我们有 h a s h hash hash 值为 H ( s ) \text{H}(s) H(s),那么我们在 s s s 后面增加一个字符 c c c 构成新的字符串 s + c s+c s+c,该串的 h a s h hash hash 值则为 H ( s ) ∗ p + ( c − ′ a ′ + 1 ) \text{H}(s)*p+(c-'a'+1) H(s)∗p+(c−′a′+1)。
如我们取 p = 131 p=131 p=131,对于字符串 ′ a b a c ′ 'abac' ′abac′, p p p 进制数为 ( 1 2 1 3 ) p (1~2~1~3)_p (1 2 1 3)p,即 h a s h hash hash 值为 1 ∗ p 3 + 2 ∗ p 2 + 1 ∗ p + 3 1*p^3+2*p^2+1*p+3 1∗p3+2∗p2+1∗p+3。此时我们的字符串更新为 ′ a b a c d ′ 'abacd' ′abacd′,那么我们的 ′ a b a c ′ 'abac' ′abac′ 需要左移一位, p p p 进制数变为 ( 1 2 1 3 0 ) p (1~2~1~3~0)_p (1 2 1 3 0)p,再加上字符 ‘ d ‘ `d` ‘d‘ 的值 ( ′ d ′ − ′ a ′ + 1 ) ('d'-'a'+1) (′d′−′a′+1), p p p 进制数最后为 ( 1 2 1 3 4 ) p (1~2~1~3~4)_p (1 2 1 3 4)p, h a s h hash hash 值则为 H ( ′ a b a c ′ ) ∗ p + ( ′ d ′ − ′ a ′ + 1 ) \text{H}('abac')*p+('d'-'a'+1) H(′abac′)∗p+(′d′−′a′+1)
所以对于一个字符串,我们需要取他的子串时,例如,对于字符串 s s s,我们需要取其子串 s l , r s_{l,r} sl,r 的 h a s h hash hash 值,我们可以用 H ( s 0 , r ) − H ( s 0 , l − 1 ) ∗ p r − l + 1 \text{H}(s_{0,r})-\text{H}(s_{0,l-1})*p^{r-l+1} H(s0,r)−H(s0,l−1)∗pr−l+1。
如 s 0 , r = ′ a b a c ′ s_{0,r}='abac' s0,r=′abac′, s 0 , l − 1 = ′ a b ′ s_{0,l-1}='ab' s0,l−1=′ab′,所以 H ( s 0 , r ) = ( 1 2 1 3 ) p \text{H}(s_{0,r})=(1~2~1~3)_p H(s0,r)=(1 2 1 3)p, H ( s 0 , l − 1 ) = ( 1 2 ) p \text{H}(s_{0,l-1})=(1~2)_p H(s0,l−1)=(1 2)p,所以对于 s l , r = ′ a c ′ s_{l,r}='ac' sl,r=′ac′,先将 H ( s 0 , l − 1 ) = ( 1 2 ) p \text{H}(s_{0,l-1})=(1~2)_p H(s0,l−1)=(1 2)p 向左移 l e n g t h ( s l , r ) length(s_{l,r}) length(sl,r) 位,变成 H ( s 0 , l − 1 ) ∗ p 2 = ( 1 2 0 0 ) p \text{H}(s_{0,l-1})*p^2=(1~2~0~0)_p H(s0,l−1)∗p2=(1 2 0 0)p,最后 H ( s 0 , r ) − H ( s 0 , l − 1 ) ∗ p 2 = ( 1 3 ) p = H ( ′ a c ′ ) = H ( s l , r ) \text{H}(s_{0,r})-\text{H}(s_{0,l-1})*p^2=(1~3)_p=\text{H}('ac')=\text{H}(s_{l,r}) H(s0,r)−H(s0,l−1)∗p2=(1 3)p=H(′ac′)=H(sl,r)
当 p p p 很大且字符串很长时,字符串的 h a s h hash hash 值也会很大,这时候一般需要设计一个值 m m m 对字符串的 h a s h hash hash 值进行取模,有 2 2 2 种处理方法:
总体来说,字符串 h a s h hash hash很难构造能卡的数据,相对来说第一个和双 h a s h hash hash碰撞概率更小一些,且由于不需要取模,第一个方法常数会小一点。
// acwing 兔子与兔子
#include
// using namespace std;
#define ull unsigned long long
#define p 131
int n;
char s[1000005];
ull b[1000005];
ull hash[1000005];
int main() {
b[0] = 1; // p的0次方
hash[0] = 0;
scanf("%s", s);
int len = strlen(s);
for (int i = 1; i <= len + 3; i++) {
b[i] = b[i - 1] * p;
hash[i] = hash[i - 1] * p + (s[i - 1] - 'a' + 1);
}
scanf("%d", &n);
for (int i = 0, l1, l2, r1, r2; i < n; i++) {
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
ull s1 = hash[r1] - hash[l1 - 1] * b[r1 - l1 + 1];
ull s2 = hash[r2] - hash[l2 - 1] * b[r2 - l2 + 1];
if (s1 == s2) {
puts("YES");
} else
puts("NO");
}
return 0;
}
可以类比二维前缀和,注意横向 h a s h hash hash 和纵向 h a s h hash hash 所用的 p p p 值不要相同。
// UVa 11019
#include
using namespace std;
#define ull unsigned long long
#define p1 131
#define p2 1331
int n, m, x, y;
char s1[1005][1005], s2[105][105];
ull h[1005][1005], h2[105][105], b1[1005], b2[1005];
void solve() {
int ans = 0;
scanf("%d%d", &n, &m);
getchar();
for (int i = 1; i <= n; i++) scanf("%s", s1[i]);
scanf("%d%d", &x, &y);
getchar();
for (int i = 1; i <= x; i++) scanf("%s", s2[i]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
h[i][j] = h[i][j - 1] * p1 + (int)(s1[i][j - 1] - 'a' + 1);
for (int j = 1; j <= m; j++)
for (int i = 1; i <= n; i++) h[i][j] += h[i - 1][j] * p2;
for (int i = 1; i <= x; i++)
for (int j = 1; j <= y; j++)
h2[i][j] = h2[i][j - 1] * p1 + (int)(s2[i][j - 1] - 'a' + 1);
for (int j = 1; j <= y; j++)
for (int i = 1; i <= x; i++) h2[i][j] += h2[i - 1][j] * p2;
ull val = h2[x][y];
// printf("# %llu #\n", val);
for (int i = x; i <= n; i++) {
for (int j = y; j <= m; j++) {
ull tmp = h[i][j] - h[i - x][j] * b2[x] - h[i][j - y] * b1[y] +
h[i - x][j - y] * b1[y] * b2[x];
if (tmp == val) ++ans;
}
}
printf("%d\n", ans);
}
int main() {
b1[0] = b2[0] = 1;
for (int i = 1; i < 1000 + 3; i++) {
b1[i] = b1[i - 1] * p1;
b2[i] = b2[i - 1] * p2;
}
int t;
scanf("%d", &t);
while (t--) solve();
return 0;
}
参考《算法竞赛进阶指南》
hdu 1711
poj 1200
poj 3461
UVa 11019