牛客NC13610 矩阵 - 二维(矩阵)哈希 + 二分
作者:互联网
一维哈希可以将字符串映射为一个整数,而二维哈希则可以将矩阵映射为一个整数。
令h[i][j]表示左上顶点为(1, 1),右下顶点为(i, j)的矩阵的哈希值,mat[i][j]表示原矩阵。
采用如下的方式更新h[i][j]:
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
h[i][j] = h[i][j - 1] * base1 + mat[i][j];
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];
其实第一次更新时h[i][j]是表示mat[i][1] ~ mat[i][j]子串的哈希值,到了第二次更新h[i][j]才表示上面所定义的意义。二维哈希与一维哈希都采用了进制哈希的思想,因此应该不难理解。
二维哈希获取子矩阵的哈希值的方式如下:
/**
* (i, j)表示矩阵的右下顶点
* leni表示矩阵沿着i所在坐标轴的边长
* lenj表示矩阵沿着j所在坐标轴的边长
*/
unsigned long long get_submat_hash(int i, int j, int leni, int lenj){
// p1[i]表示base1的i次方, p2[i]表示base2的i次方
return h[i][j] - h[i - leni][j] * p2[leni] - h[i][j - lenj] * p1[lenj] + h[i - leni][j - lenj] * p1[leni] * p2[lenj];
}
在本题中,在计算出原矩阵的二维哈希值之后,我们可以二分枚举正方形的边长k,然后枚举所有边长为k的正方形的哈希值,并用map记录其出现次数,如果某一个哈希值出现了两次,则代表存在边长为k的正方形出现了至少两次,更新答案并增大k,否则减小k。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#define _min(x, y) x < y ? x : y
#define _max(x, y) x > y ? x : y
using namespace std;
typedef unsigned long long ull;
const int maxn = 505;
const ull basen = 233333;
const ull basem = 13331;
char mat[maxn][maxn];
ull h[maxn][maxn], pn[maxn], pm[maxn];
map<ull, int> mp;
void hash(int n, int m){
h[0][0] = 0;
pn[0] = pm[0] = 1;
for(int i = 1; i <= m; ++i){
h[0][i] = 0;
pm[i] = pm[i - 1] * basem;
}
for(int i = 1; i <= n; ++i){
h[i][0] = 0;
pn[i] = pn[i - 1] * basen;
}
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
h[i][j] = h[i][j - 1] * basem + (ull)mat[i][j];
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
h[i][j] = h[i - 1][j] * basen + h[i][j];
}
ull get_submat_hash(int x, int y, int len){
return h[x][y] - h[x - len][y] * pn[len] - h[x][y - len] * pm[len] + h[x - len][y - len] * pn[len] * pm[len];
}
bool check(int len, int n, int m){
mp.clear();
for(int i = len; i <= n; ++i){
for(int j = len; j <= m; ++j){
ull _hash = get_submat_hash(i, j, len);
mp[_hash]++;
if(mp[_hash] >= 2)
return true;
}
}
return false;
}
int main(){
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
scanf("%s", mat[i] + 1);
hash(n, m);
int ans = 0;
int l = 1, r = min(n, m) + 1;
while(l < r){
int mid = (l + r) >> 1;
if(check(mid, n, m)){
l = mid + 1;
ans = mid;
}
else
r = mid;
}
printf("%d\n", ans);
return 0;
}
标签:牛客,int,lenj,leni,矩阵,NC13610,maxn,哈希 来源: https://blog.csdn.net/shamansi99/article/details/113913732