字符串哈希和子串匹配
对于一个字符串来说,它的哈希值算法为
#define ULL unsigned long long
const ULL P = 13331;
ULL GetHash(char *s)
{
int Len = strlen(s);
ULL Hash = 0;
for (int i = 0; i < Len; ++i)
Hash = Hash * P + s[i] - 'a' + 1;
return Hash;
}
其中,\(P\)是选取的质数。
子串哈希
首先要预准备,将文本串的前缀哈希值存入\(f\)数组中:
for (int i = 1; i <= Len; ++i)
f[i] = f[i - 1] * P + str[i] - 'a' + 1;
得到了f[1],f[2]......之后,我们观察一下
\[f[2]=f[1]*P+h[2]
\]
\[f[3]=f[2]*P+h[3]=f[1]*p^2+h[2]*P+h[3]
\]
\[f[4]=f[3]*P+h[4]=f[1]*P^3+f[2]*P^2+h[3]*P+h[4]
\]
于是可以发现这条规律:
\[Hash_{l-r}=f[r]-f[l-1]*p^{r-l+1}
\]
ULL getHash(int l, int r)
{
return f[r] - f[l - 1] * quickpow(P, r - l + 1);
}
所以只要知道文本串的哈希值,就可以以\(O(1)\)的复杂度得出它任意子串的哈希值。进而进行匹配。
EG.Singing Superstar
https://acm.hdu.edu.cn/showproblem.php?pid=7064
#include <bits/stdc++.h>
using namespace std;
#define ULL unsigned long long
const int N = 100010;
char str[N];
//
map<ULL, int> vis;
const ULL P = 13331;
ULL Hash(char *s)
{
int Len = strlen(s);
ULL res = 0;
for (int i = 0; i < Len; ++i)
res = res * P + s[i] - 'a' + 1;
return res;
}
ULL f[N];
ULL quickpow(ULL a, ULL b)
{
ULL res = 1;
while (b)
{
if (b & 1)
res *= a;
b >>= 1;
a *= a;
}
return res;
}
ULL getHash(int l, int r)
{
return f[r] - f[l - 1] * quickpow(P, r - l + 1);
}
//
int ans[N], Last[N];
ULL A[N];
void solve()
{
//init
vis.clear();
memset(ans, 0, sizeof(ans));
memset(Last, 0, sizeof(Last));
memset(A, 0, sizeof(A));
scanf("%s", str + 1);
int Len = strlen(str + 1);
int n;
scanf("%d", &n);
char tmp_s[40];
for (int i = 1; i <= n; ++i)
{
scanf("%s", tmp_s);
vis[Hash(tmp_s)] = i;
A[i] = Hash(tmp_s);
}
//pre
for (int i = 1; i <= Len; ++i)
f[i] = f[i - 1] * P + str[i] - 'a' + 1;
//dp
for (int i = 1; i <= Len; ++i)
for (int j = 1; j <= i && j <= 30; ++j)
{
int p = vis[getHash(i - j + 1, i)];
if (p && i - j >= Last[p])
ans[p]++, Last[p] = i;
}
for (int i = 1; i <= n; ++i)
printf("%d\n", ans[vis[A[i]]]);
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
solve();
return 0;
}
posted on
浙公网安备 33010602011771号