字符串哈希和子串匹配

对于一个字符串来说,它的哈希值算法为

#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 2021-08-13 16:29  Stuart_Assassins  阅读(168)  评论(0)    收藏  举报