字符串哈希

使用unsigned long long来做自然溢出

#include <iostream>

using namespace std;

typedef unsigned long long ULL;

const int N = 1e5 + 10, P = 131;
ULL h[N], p[N];
char s[N];
int n, m;

ULL sub_hash(int l, int r) {
    return h[r] - h[l - 1] * p[r - l + 1];
}

int main() {
    scanf("%d%d\n%s\n", &n, &m, s + 1);
    p[0] = 1L;
    for (int i = 1; i <= n; ++i) {
        h[i] = h[i - 1] * P + s[i];
        p[i] = p[i - 1] * P;
    }
    while (m--) {
        int l1, r1, l2, r2;
        scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
        puts(sub_hash(l1, r1) == sub_hash(l2, r2) ? "Yes" : "No");
    }

    return 0;
}
作者:GabrielxD
链接:https://www.acwing.com/solution/content/148484/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

不做自然溢出,单值hash

// 字符串哈希模板开始
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

int rnd(int x, int y) {
    return uniform_int_distribution<int>(x, y)(rng);
}

int MOD = 998244353 + rnd(0, 1e9), BASE = 233 + rnd(0, 1e3);

struct HashSeq {
    vector<long long> P, H;

    HashSeq() {}

    HashSeq(string &s) {
        int n = s.size();
        P.resize(n + 1);
        P[0] = 1;
        for (int i = 1; i <= n; i++) P[i] = P[i - 1] * BASE % MOD;
        H.resize(n + 1);
        H[0] = 0;
        for (int i = 1; i <= n; i++) H[i] = (H[i - 1] * BASE + (s[i - 1] ^ 7)) % MOD;
    }

    long long query(int l, int r) {
        return (H[r] - H[l - 1] * P[r - l + 1] % MOD + MOD) % MOD;
    }
};
// 字符串哈希模板结束

class Solution {
public:
    long long countPrefixSuffixPairs(vector<string>& words) {
        int n = words.size();
        vector<HashSeq> hs;
        for (auto &s : words) hs.push_back(HashSeq(s));

        int mx = 0;
        for (auto &s : words) mx = max(mx, (int) s.size());
        // cnt[l][h] 表示目前有几个长度为 l,且哈希值为 h 的字符串
        unordered_map<long long, int> cnt[mx + 1];

        long long ans = 0;
        // 枚举每个字符串
        for (int i = 0; i < n; i++) {
            int len = words[i].size();
            // 枚举每个前缀
            for (int j = 1; j <= len; j++) {
                long long h1 = hs[i].query(1, j), h2 = hs[i].query(len - j + 1, len);
                // 首先检查这个前缀是否也是后缀,如果是,则答案增加
                if (h1 == h2 && cnt[j].count(h1)) ans += cnt[j][h1];
            }
            // 把当前字符串加入哈希表
            cnt[len][hs[i].query(1, len)]++;
        }
        return ans;
    }
};

作者:TsReaper
链接:https://leetcode.cn/circle/discuss/ZnbNSt/view/nXnhq5/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

双值hash

namespace utils {

static int D = 127;
static int mod1 = 1e9 + 7, mod2 = 1e9 + 9;
vector<long long> pow1, pow2;
void init(int l) {
  pow1.resize(l + 1, 0);
  pow1[0] = 1; pow2 = pow1;
  for (int i = 1; i <= l; i ++)
    pow1[i] = pow1[i - 1] * D % mod1,
    pow2[i] = pow2[i - 1] * D % mod2;
}
int qmod(int v, int m) {
  return v + ((v >> 31) & m);
}
struct Hash {
  vector<long long> val1, val2;
  Hash(string s) {
    val1.reserve(s.size());
    val2.reserve(s.size());
    long long v1 = 0, v2 = 0;
    for (auto c: s) {
      v1 = (v1 * D + c) % mod1;
      v2 = (v2 * D + c) % mod2;
      val1.push_back(v1);
      val2.push_back(v2);
    }
  }
  pair<int, int> val() {
    return {val1.back(), val2.back()};
  }
  pair<int, int> val(int l, int r) {
    if (l == 0)
      return {val1[r], val2[r]};
    return {
      qmod(val1[r] - val1[l - 1] * pow1[r - l + 1] % mod1, mod1),
      qmod(val2[r] - val2[l - 1] * pow2[r - l + 1] % mod2, mod2)
    };
  }
};

}

using utils::Hash;
using utils::init;
pair<int, int> H(string s) {
  return Hash(s).val();
}

class Solution {
public:
    long long countPrefixSuffixPairs(vector<string>& words) {
        init(1e5);
        map<pair<int, int>, int> mp;
        long long ans = 0;
        for (int i = 0; i < (int)words.size(); i ++) {
            auto h = Hash(words[i]);
            for (int l = 1; l <= (int)words[i].size(); l ++) {
                if (h.val(0, l - 1) == h.val(words[i].size() - l, words[i].size() - 1))
                    ans += mp[h.val(0, l - 1)];
            }
            ++ mp[h.val()];
        }
        return ans;
    }
};
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值