牛客 矩阵 二维哈希+二分
程序员文章站
2024-03-17 15:34:04
...
题意
给出一个n * m的矩阵。让你从中发现一个最大的正方形。使得这样子的正方形在矩阵中出现了至少两次。输出最大正方形的边长。
分析
这题关键点就是如何去定义正方形,如果纯粹去暴力,将矩阵逐行去分析,那么复杂度将会到达O(n3 ),如果知道二维哈希,那么获得正方形的哈希值就可以在O(1)的时间复杂度下得到了。
二维哈希的关键就是要定义和获取,定义如下:
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
hasha[i][j]=hasha[i][j-1]*base1+a[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
hasha[i][j]+=hasha[i-1][j]*base2;
获取如下:
k = Hash[i][j]-Hash[i][j-x]*hash1[x]-Hash[i-x][j]*hash2[x]+Hash[i-x][j-x]*hash1[x]*hash2[x];
//这个的意思就是右下角为(i,j),边长为x的正方形
第一次是横着hash,用的是base1,此时的hash[i][j]表示的是第i行长度为j的前缀串的hash值。
第二次是竖着hash,用的是base2,此时的hash[i][j]发生了更新,此时的hash[i][j]变成了(1,1)到(i-1,j)矩阵的hash值*base2+第i行长度为j的前缀串的hash值,表示的是(1,1)到(i,j)矩阵的hash值。
然后求一个子矩阵的hash值时,比如子矩阵的右下角坐标为(i,j),行数为n,列数为m,则子矩阵的hash值为hash[i][j]-hash[i-n][j]*base1n -hash[i][j-m]*base2m+hash[i-n][j-m]*base1n *base2m.(这个和二维前缀和有点类似)。
那么这个题就好实现了
#include <iostream>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
#include <set>
#include <cstring>
#include <stack>
#include <string>
using namespace std;
typedef unsigned long long ll;
const int N = 1e5+199;
const double Pi = acos(-1);
int n,m;
char e[600][600];
map<ll,bool>ma;
ll base1 = 131, base2 = 137, hash1[510], hash2[510], Hash[510][510];
bool Check(int x){
for(int i=x;i<=n;i++){
for(int j=x;j<=m;j++){
ll k = Hash[i][j]-Hash[i][j-x]*hash1[x]-Hash[i-x][j]*hash2[x]+Hash[i-x][j-x]*hash1[x]*hash2[x];
// cout<<k<<endl;
if(ma[k])
return true;
else
ma[k]=1;
}
}
return false;
}
void Init(){
hash1[0]=hash2[0]=1;
for(int i=1;i<=500;i++){
hash1[i] = hash1[i-1]*base1;
hash2[i] = hash2[i-1]*base2;
}
for(int i=1;i<=n;i++){//对每一行进行哈希
for(int j=1;j<=m;j++){
Hash[i][j]=Hash[i][j-1]*base1 + e[i][j]-'a'+1;
}
}
for(int i=1;i<=n;i++){//类似二维求和,相当于对每一列哈希
for(int j=1;j<=m;j++){
Hash[i][j]+=Hash[i-1][j]*base2;
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif // ONLINE_JUDGE
scanf("%d%d\n",&n,&m);
for(int i=1;i<=n;i++)
scanf("%s",e[i]+1);
Init();
int lb=1,ub=min(n,m),mid;
while(lb<=ub){
mid = (lb+ub)>>1;
if(Check(mid))
lb = mid+1;
else
ub = mid-1;
ma.clear();
// cout<<mid<<endl;
}
printf("%d\n",lb-1);
return 0;
}
上一篇: POJ 3258 River Hopscotch(二分)
下一篇: 牛客网——晾衣服(二分)