博客链接:
字符串哈希模板:
struct stringhash{
LL h[N], w[N], base, mod;
void init(LL b, LL m){ // 基数模数初始化
h[0] = 0, w[0] = 1, base = b, mod = m;
for(int i=1; i<N; i++) w[i] = w[i - 1] * base % mod;
}
void add(char * str) // 构造哈希表
{
int len = strlen(str + 1);
for(int i=1; i<=len; i++)
h[i] = (h[i - 1] * base + str[i]) % mod;
}
LL get(int l, int r){ // 获得区间哈希
return (h[r] - h[l - 1] * w[r - l + 1] % mod + mod) % mod;
}
}ha1, ha2;
更新模板:
cstr_hash:
struct cstr_hash{
LL h[N], w[N], n, base, mod;
void init(LL b, LL m){ // 基数模数初始化
h[0] = 0, w[0] = 1, base = b, mod = m;
for(int i = 1; i < N; i ++ )
w[i] = w[i - 1] * base % mod;
}
void add(char * str){ // 构造哈希表
n = strlen(str + 1);
for(int i = 1; i <= n; i ++ )
h[i] = (h[i - 1] * base + str[i]) % mod;
}
LL get(int l, int r){ // 获得区间哈希
return (h[r] - h[l - 1] * w[r - l + 1] % mod + mod) % mod;
}
LL pre(int len){
return get(1, len);
}
LL suf(int len){
return get(n - len + 1, n);
}
}ha;
str_hash:
struct str_hash{
LL h[N], w[N], n, base, mod;
void init(LL b, LL m){ // 基数模数初始化
h[0] = 0, w[0] = 1, base = b, mod = m;
for(int i = 1; i < N; i ++ )
w[i] = w[i - 1] * base % mod;
}
void add(string & str){
n = str.size();
if(n) h[0] = str[0] % mod;
for(int i = 1; i < n; i ++ )
h[i] = (h[i - 1] * base + str[i]) % mod;
}
LL get(int l, int r){ // 获得区间哈希
if(!l) return h[r];
return (h[r] - h[l - 1] * w[r - l + 1] % mod + mod) % mod;
}
LL pre(int len){
return get(0, len - 1);
}
LL suf(int len){
return get(n - len, n - 1);
}
}ha;
注意事项:
base
和mod
尽可能不要取小。mod
可以使用不常用的大质数,防止常用质数被卡。- 使用一个哈希表可能会产生碰撞,担心碰撞的话使用双哈希,二元组
pair<LL, LL>
即为哈希值。
例题:
2020年河南省CCPC K.子串翻转回文串
-
题解:https://blog.csdn.net/weixin_43731933/article/details/109989821
-
小tips:如何多次 O ( 1 ) O(1) O(1) 询问区间子串是否回文?
-
将字符串正、反映射到两个哈希表中(注意基数和模数要相同),
询问时判断 h a 1 [ l , r ] ha1~[l,r] ha1 [l,r] 和 h a 2 [ n − r + 1 , n − l + 1 ] ha2~[n-r+1,n-l+1] ha2 [n−r+1,n−l+1] 是否相等,相等即回文。
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=5e5+10;
struct stringhash{
LL h[N], w[N], base, mod;
void init(LL b, LL m){
h[0] = 0, w[0] = 1, base = b, mod = m;
}
void add(char * str)
{
int len = strlen(str + 1);
for(int i=1; i<=len; i++)
h[i] = (h[i - 1] * base + str[i]) % mod, w[i] = w[i - 1] * base % mod;
}
LL get(int l, int r){
return (h[r] - h[l - 1] * w[r - l + 1] % mod + mod) % mod;
}
}ha1, ha2;
char str[N], s[N];
int n;
bool check()
{
if(n == 0) return 1;
for(int i=1; i <= n; i++)
{
if(i <= n - i)
{
if(ha1.get(1, i) == ha1.get(n - i + 1, n))
if(ha1.get(i + 1, n - i) == ha2.get(i + 1, n - i)) return 1;
}else
{
if(ha1.get(i + 1, n) == ha1.get(2 * i - n + 1, i))
if(ha1.get(1, 2 * i - n) == ha2.get(2 * n - 2 * i + 1, n)) return 1;
}
}
return 0;
}
int main()
{
ha1.init(1213, 50331653);
ha2.init(1213, 50331653);
int T; scanf("%d", &T);
while(T--)
{
scanf("%s", str + 1);
n = 0;
int len = strlen(str + 1);
for(int i=1; i <= len; i++)
if(str[i] != str[len - i + 1]){
for(int j=i; j <= len - i + 1; j++){
s[++n] = str[j];
}
break;
}
ha1.add(s);
reverse(s + 1, s + 1 + n);
ha2.add(s);
int ans = check();
if(ans == 0)
{
ha1.add(s);
reverse(s + 1, s + 1 + n);
ha2.add(s);
ans = check();
}
printf("%s\n", ans ? "Yes" : "No");
}
system("pause");
return 0;
}
【二维哈希】Matrix
- 学习链接:二维hash
- 牛客 矩阵 二维哈希+二分
代码(被卡内存了,不知道怎么优化):
- 更新:unordered_map换成vector内存少了,但是超时,估计是LL或模的问题。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e3 + 10;
int n, m, a, b;
char w[N][N];
LL res;
struct stringhash{
LL h[N][N], w1[N], w2[N], base1, base2, mod;
void init(LL b1, LL b2, LL _m){
base1 = b1, base2 = b2, mod = _m;
w1[0] = w2[0] = 1;
for(int i=1; i<N; i++) w1[i] = w1[i - 1] * base1 % mod;
for(int i=1; i<N; i++) w2[i] = w2[i - 1] * base2 % mod;
}
inline void add()
{
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
h[i][j] = h[i][j - 1] * base1 % mod + w[i][j], h[i][j] %= mod;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
h[i][j] += h[i - 1][j] * base2, h[i][j] %= mod;
}
inline void get(int & i, int & j){
res = ((h[i][j] - h[i][j - b] * w1[b] - h[i - a][j] * w2[a] + h[i - a][j - b] * w1[b] % mod * w2[a]) % mod + mod) % mod;
}
}ha;
unordered_map<LL, bool>mp;
int main()
{
ha.init(114, 514, 161061241);
scanf("%d %d %d %d", &n, &m, &a, &b);
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
scanf(" %c", &w[i][j]);
ha.add();
for(int i=a; i<=n; i++)
for(int j=b; j<=m; j++){
ha.get(i, j);
mp[res] = 1;
}
int T; scanf("%d", &T);
while(T--)
{
for(int i=1; i<=a; i++)
for(int j=1; j<=b; j++) scanf(" %c", &w[i][j]);
ha.add();
if(mp[ha.h[a][b]]) printf("1\n");
else printf("0\n");
}
system("pause");
return 0;
}
AC代码(用的unsigned int):
- 这里用vector内存为10M,用unordered_map内存为32M。可能哈希要占内存吧。vector空间常数应该是更优的
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned int UI;
const int N=1e3 + 10;
int n, m, a, b;
char w[N][N];
UI res;
struct stringhash{
UI h[N][N], w1[N], w2[N], base1, base2;
void init(UI b1, UI b2){
base1 = b1, base2 = b2;
w1[0] = w2[0] = 1;
for(int i=1; i<N; i++) w1[i] = w1[i - 1] * base1;
for(int i=1; i<N; i++) w2[i] = w2[i - 1] * base2;
}
inline void add()
{
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
h[i][j] = h[i][j - 1] * base1 + w[i][j];
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
h[i][j] += h[i - 1][j] * base2;
}
inline void get(int i, int j){
res = h[i][j] - h[i][j - b] * w1[b] - h[i - a][j] * w2[a] + h[i - a][j - b] * w1[b] * w2[a];
}
}ha;
vector<UI>vec;
int main()
{
ha.init(131, 233);
scanf("%d %d %d %d", &n, &m, &a, &b);
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
scanf(" %c", &w[i][j]);
ha.add();
for(int i=a; i<=n; i++)
for(int j=b; j<=m; j++){
ha.get(i, j);
vec.push_back(res);
}
sort(vec.begin(), vec.end());
int T; scanf("%d", &T);
n = a, m = b;
while(T--)
{
for(int i=1; i<=a; i++)
for(int j=1; j<=b; j++) scanf(" %c", &w[i][j]);
ha.add();
if(find(vec.begin(), vec.end(), ha.h[a][b]) != vec.end()) printf("1\n");
else printf("0\n");
}
system("pause");
return 0;
}