题意:给出一个n * m的矩阵。让你从中发现一个最大的正方形。使得这样子的正方形在矩阵中出现了至少两次。输出最大正方形的边长。
题解:首先我们如何记录一个矩阵的状态,这时候我们需要用到二维hash。二维hash首先是对行进行hash,然后对列进行再hash。当然,两次hash要用到不一样的质数:
for(int i=1;i<=n;i++)//二维hash
for(int j=1;j<=m;j++)
Hash[i][j]=Hash[i][j-1]*basem+a[i][j];
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
Hash[j][i]=Hash[j-1][i]*basen+Hash[j][i];
对于确定对角顶点的矩阵,我们可以这样获得这个矩阵的hash值:
ll ha=Hash[i][j]-Hash[i][j-mm]*hashm[mm]-Hash[i-nn][j]*hashn[nn]+Hash[i-nn][j-mm]*hashm[mm]*hashn[nn];
假如暴力所有类型的正方形矩阵,我们要n^3的复杂度,这时候我们可以二分答案,再暴力计算当前答案是否存在就可以得到n^2*logn的复杂度。
AC代码:
#include<stdio.h>
#include<map>
#include<queue>
#include<algorithm>
#define N 505
using namespace std;
typedef long long ll;
map<ll,int>mp;
char a[N][N];
int n,m;
ll Hash[N][N];
ll hashn[N],hashm[N],basen=1e9+7,basem=1e9+9;
bool judge(int len)
{
for(int i=len;i<=n;i++)
for(int j=len;j<=m;j++)
{
ll ha=Hash[i][j]-Hash[i][j-len]*hashm[len]-Hash[i-len][j]*hashn[len]+Hash[i-len][j-len]*hashm[len]*hashn[len];
if(mp[ha]==1)return true;
mp[ha]=1;
}
return false;
}
ll b[N*N];
int main()
{
hashm[0]=hashn[0]=1;
for(int i=1;i<N;i++)
{
hashm[i]=hashm[i-1]*basem;
hashn[i]=hashn[i-1]*basen;
}
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%s",a[i]+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
Hash[i][j]=Hash[i][j-1]*basem+a[i][j];
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
Hash[j][i]=Hash[j-1][i]*basen+Hash[j][i];
int l=1,r=min(n,m);
while(l<=r)
{
mp.clear();
int mid=l+r>>1;
if(judge(mid))l=mid+1;
else r=mid-1;
}
printf("%d\n",r);
return 0;
}