算法 - 字符串算法 - 字符串哈希与经典例题《好文章》

nodgd 写了一篇文章,自认为这是一篇好文章。nodgd 的文章由 \(n\) 个小写英文字母组成。文章的一个子串指的是文章中的一段连续的字母,子串的长度就是这一段的字母个数。nodgd 在文章中用了排比、对偶、前后照应之类的手法,所以就有很多个子串是相同或者相近的。为了向大家证明这是一篇好文章,nodgd 决定给自己的文章进行评分。nodgd 首先确定了一个整数 \(m\),然后统计出文章中有多少个不相同的长度为 \(m\) 的子串,这个数量就是文章的评分。然而,nodgd 懒得老老实实计算这个评分了,就把任务丢给了你。

输入格式

第一行包含两个整数 \(n,m\),表示文章的长度和需要统计的子串长度。

第二行包含一个长度为 \(n\) 的只包含小写字母的字符串。

输出格式

输出一行一个整数,表示文章的评分。

输入输出样例

样例 1

输入

5 3
aaaab

输出

2

长度为 \(3\) 的子串有 \(3\) 个,分别是 aaa、aaa、aab,其中不同的只有 \(2\) 个。

样例 2

输入

9 3
abcabacba

输出

7

共有7个长度为3的子串,每个长度为3的子串都不同。

数据范围

\(30\%\) 的数据,\(1 ≤ m ≤ n ≤ 200\)

\(50\%\) 的数据,\(1 ≤ m ≤ n ≤ 2000\)

\(70\%\) 的数据,\(1 ≤ m ≤ 50 ≤ n ≤ 200000\)

\(100\%\) 的数据,\(1 ≤ m ≤ n ≤ 200000\)

题解

使用双哈希,这里我们开两个hash表,然后给同一个字符串附上两个hash值,在判断两个字符串是否相同时,必须两个hash值都相同才行。

#include<cmath>
#include<cstdio>

const int maxm = 200010;
const int mod1 = 1e9 + 7, mod2 = 1e9 + 9;

long long hash_a[maxm], hash_b[maxm];
long long Hash_a[maxm], Hash_b[maxm];         // 字符串 s[1..i] 的哈希值
long long Hash_pow_a[maxm], Hash_pow_b[maxm]; // 指数数组

int n, m, ans;
char str[maxm];

// 利用给定的哈希算法参数,算出给定串的哈希值(包括哈希数组和指数数组)
void Init(int p1, int P1, int p2, int P2)
{
    for (int i = 1; i <= n; i++)
    {
        // 获取第一个哈希的结果
        Hash_a[i] = (1LL * Hash_a[i - 1] * p1 + 1LL * (str[i] - 'a' + 1)) % P1;
        Hash_pow_a[i] = 1LL * Hash_pow_a[i - 1] * p1 % P1;
        // 获取第二个哈希的结果
        Hash_b[i] = (1LL * Hash_b[i - 1] * p2 + 1LL * (str[i] - 'a' + 1)) % P2;
        Hash_pow_b[i] = 1LL * Hash_pow_b[i - 1] * p2 % P2;
    }
}

// 获取子串的哈希值
int GetHash(int l, int r, int p, int P, int k)
{
    if (k == 1) {      // 说明函数请求获取该子串的第一个哈希值
        return (Hash_a[r] - 1LL * Hash_a[l - 1] * Hash_pow_a[r - l + 1] % P + P) % P;
    }
    else if (k == 2){  // 说明函数请求获取该子串的第二个哈希值
        return (Hash_b[r] - 1LL * Hash_b[l - 1] * Hash_pow_b[r - l + 1] % P + P) % P;
    }
}

//快排,可以使用sort,开结构体,然后根据HashA的值排序
void qst(int l, int r)
{
    int i, j, m, mm, t;
    i = l; j = r;
    m = hash_a[(i + j) >> 1];//位运算,相当于除以2
    mm = hash_b[(i + j) >> 1];
    while (i <= j)
    {
        while (hash_a[i] < m || (hash_a[i] == m && hash_b[i] < mm))i++;
        while (hash_a[j] > m || (hash_a[j] == m && hash_b[j] > mm))j--;
        if (i <= j)
        {
            t = hash_a[i]; hash_a[i] = hash_a[j]; hash_a[j] = t;
            t = hash_b[i]; hash_b[i] = hash_b[j]; hash_b[j] = t;
            i++; j--;
        }
    }
    if (i < r) qst(i, r);
    if (l < j) qst(l, j);
}

int main()
{
    scanf("%d%d", &n, &m);
    scanf("%s", str + 1);
    Hash_pow_a[0] = 1;
    Hash_pow_b[0] = 1;
    Init(61, mod1, 97, mod2);
    for (int i = 1; i <= n - m + 1; i++)
    {
        hash_a[i] = GetHash(i, i + m - 1, 61, mod1, 1);
        hash_b[i] = GetHash(i, i + m - 1, 97, mod2, 2);
    }
    qst(1, n - m + 1);
    for (int i = 1; i <= n - m + 1; i++)
    {
        if (hash_a[i] == hash_a[i - 1] && hash_b[i] == hash_b[i - 1])
            continue;
        ans++;
    }
    printf("%d\n", ans);
    return 0;
}

posted on 2022-04-17 16:18  Black_x  阅读(161)  评论(0编辑  收藏  举报