【模板】字符串哈希 双模+自然溢出

【模板】字符串哈希

题目描述

如题,给定 N N N 个字符串(第 i i i 个字符串长度为 M i M_i Mi,字符串内包含数字、大小写字母,大小写敏感),请求出 N N N 个字符串中共有多少个不同的字符串。

输入格式

第一行包含一个整数 N N N,为字符串的个数。

接下来 N N N 行每行包含一个字符串,为所提供的字符串。

输出格式

输出包含一行,包含一个整数,为不同的字符串个数。

样例 #1

样例输入 #1

5
abc
aaaa
abc
abcc
12345

样例输出 #1

4

提示

对于 30 % 30\% 30% 的数据: N ≤ 10 N\leq 10 N10 M i ≈ 6 M_i≈6 Mi6 M m a x ≤ 15 Mmax\leq 15 Mmax15

对于 70 % 70\% 70% 的数据: N ≤ 1000 N\leq 1000 N1000 M i ≈ 100 M_i≈100 Mi100 M m a x ≤ 150 Mmax\leq 150 Mmax150

对于 100 % 100\% 100% 的数据: N ≤ 10000 N\leq 10000 N10000 M i ≈ 1000 M_i≈1000 Mi1000 M m a x ≤ 1500 Mmax\leq 1500 Mmax1500

样例说明:

样例中第一个字符串(abc)和第三个字符串(abc)是一样的,所以所提供字符串的集合为{aaaa,abc,abcc,12345},故共计4个不同的字符串。

双模哈希模板

进制哈希。进制哈希的核心便是给出一个固定进制base,将一个串的每一个元素看做一个进制位上的数字,所以这个串就可以看做一个base进制的数,那么这个数就是这个串的哈希值;则我们通过比对每个串的的哈希值,即可判断两个串是否相同

#include<iostream>
#include<string>
#include<vector>
#include<queue>
#include<unordered_map>
#include<unordered_set>
#include<map>
#include<set>
#include<algorithm>
#include<cmath>
#include<random>
#include<ctime>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;

const int mod1 = 1e9 + 7;
const int mod2 = 1e9 + 9;
vector<pii> pw;
pii base;
mt19937 rnd(time(0));

pii operator+(const pii& a, const pii& b) {
    int c1 = a.fi + b.fi, c2 = a.se + b.se;
    if (c1 >= mod1) c1 -= mod1;
    if (c2 >= mod2) c2 -= mod2;
    return { c1, c2 };
}

pii operator-(const pii& a, const pii& b) {
    int c1 = a.fi - b.fi, c2 = a.se - b.se;
    if (c1 < 0) c1 += mod1;
    if (c2 < 0) c2 += mod2;
    return { c1, c2 };
}

pii operator*(const pii& a, const pii& b) {
    return { 1LL * a.fi * b.fi % mod1, 1LL * a.se * b.se % mod2 };
}

void init_strhash(int lim = 0) {
    pw = vector<pii>(lim + 1);
    base = { rnd() % mod1, rnd() % mod2 };
    pw[0] = { 1, 1 };
    for (int i = 1; i <= lim; i++) pw[i] = pw[i - 1] * base;
}

struct Str_hash {
    vector<pii> v;

    Str_hash() {}

    // 下标 1 开始
    void init(const string& s) {
        int m = s.size();
        v.resize(m + 1);
        for (int j = 1; j <= m; j++) {
            char ch = s[j - 1];
            v[j] = v[j - 1] * base + make_pair(ch, ch);
        }
    }

    pii get(int L, int R) {
        return v[R] - (v[L - 1] * pw[R - L + 1]);
    }
};

int main()
{
    vector<pii> ans;
    int n;
    cin >> n;
    init_strhash(n);
    vector<string> vs(n);
    for (int i = 0; i < n; i++) cin >> vs[i];
    for (int i = 0; i < n; i++) {
        Str_hash hs;
        hs.init(vs[i]);
        ans.push_back(hs.get(1, vs[i].size()));
    }
    sort(ans.begin(), ans.end());
    int res = n;
    for (int i = 0; i < ans.size() - 1; i++) {
        if (ans[i].fi == ans[i + 1].fi && ans[i].se == ans[i + 1].se) --res;
    }
    cout << res;
    return 0;
}

自然溢出哈希模板

#include<iostream>
#include<cstring>
typedef unsigned long long ULL;
int n, m;
const int N = 1e5 + 5, P = 131;
ULL h[N], p[N];
char s[N];
using namespace std;
ULL get(int l, int r) {
    return h[r] - h[l - 1] * p[r - l + 1];
}
int main()
{
    cin >> n >> m;
    scanf("%s", s + 1);
    p[0] = 1;
    for(int i = 1;i <= n;i++) {
        p[i] = p[i - 1] * P;
        h[i] = h[i - 1] * P + s[i];
    }
    while(m--) {
        int l1, r1, l2, r2;
        cin >> l1 >> r1 >> l2 >> r2;
        if(get(l1, r1) == get(l2, r2)) cout << "Yes" << endl;
        else cout << "No" << endl;
    }
    return 0;
}
字符串哈希滑动窗口是一种用于处理字符串算法。它主要用于在给定的字符串中找到满足特定条件的子串。 在字符串哈希滑动窗口算法中,我们首先计算原始字符串哈希值。然后,我们使用一个滑动窗口来遍历字符串,每次滑动一个固定长度的窗口。我们可以通过比较每个窗口内的子串的哈希值来判断是否满足条件。 具体而言,我们可以使用BKDRHash等哈希函数来计算字符串哈希值。然后,我们枚举每个可能的起点,并使用滑动窗口来计算窗口内的子串的哈希值。通过比较窗口内的子串的哈希值,我们可以判断是否满足条件。 对于滑动窗口的移动,如果窗口内的子串满足条件,我们可以继续将窗口往右移动一个固定的长度。如果窗口内的子串不满足条件,我们将窗口的右边界移到最右端,并依次比较新窗口内的子串的哈希值。 综上所述,字符串哈希滑动窗口算法是通过计算字符串哈希值,并使用滑动窗口来遍历字符串,以找到满足特定条件的子串。这个算法可以高效地处理字符串,并且能够应用于各种字符串相关的问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [String (字符串哈希+滑动窗口)](https://blog.csdn.net/weixin_43872264/article/details/107571742)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【字符串hash+滑动窗口】String HDU - 4821](https://blog.csdn.net/qq_45599865/article/details/111143633)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值