牛客网 Wannafly模拟赛 矩阵 二分+hash矩阵

题目链接


题意:



给出一个n * m的矩阵。让你从中发现一个最大的正方形。使得这样子的正方形在矩阵中出现了至少两次。输出最大正方形的边长。



思路:

一开始没想明白二分, 首先判断具有单调性,长度为3的正方形存在,长度为2的也一定存在...如果最大长度为6的存在,而长度为7的一定不存在.

然后就是想怎么去快速判断两个矩阵是否相等啊,肯定是hash了.于是hash我算错了复杂度,其实线性就可以hash矩阵了,我算成n3.还是姿势太少。

     

这里我对每一个位置的字母的贡献都按照 第w个位置(一行一行的 ,从1....n*m最多500*500).base^w来计算.先维护一个二维前缀和(利用容斥原理维护二维前缀和的复杂度是n2,查询任意一个一直长度矩阵的和复杂度也是n2,都利用容斥原理) 

然后二分答案,去check. check的时候发现这样hash的话每个正方形内部从1....s个位置 前一个位置比后一个多乘一个base,但是任意两个矩阵之间是差很多的,这里我们让两个矩阵对应的位置次数对齐即可.

 用了map,hash值不合适被卡常数了,复杂度 n*mlog^2 。把hash值改小才过,

     

700ms+

#include
using namespace std;
const int maxn = 500+10;
  
typedef unsigned long long ll;
  
char s[maxn][maxn];
int n,m;
  
ll x[maxn*maxn],h[maxn*maxn];
ll f[maxn][maxn];
  
void Hash(){
    memset(f,0,sizeof(f));
    h[0]=0;x[0]=1;
    int k = 0;
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++){
        ++k;
        x[k]=x[k-1]*19;
        f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+x[k]*(s[i][j]-'a');
    }
}
mapmp;
ll tmp ;
bool check(int ans){
    mp.clear();
    for(int i=ans;i<=n;i++){
        for(int j=ans;j<=m;j++){
            tmp = f[i][j]-f[i-ans][j]-f[i][j-ans]+f[i-ans][j-ans];
            tmp = tmp*x[(n-i)*m+m-j];
            if(mp.count(tmp)) return true;
            mp[tmp]=1;
        }
    }
    return false;
}
int main(){
    while(~scanf("%d%d",&n,&m)){
          
        for(int i=1;i<=n;i++)
        scanf("%s",s[i]+1);
          
        Hash();
          
        int l=1,r=min(n,m),ans=0,mid;
          
        while(l<=r){
            mid = r+l >> 1;
            if(check(mid)) ans = mid,l=mid+1;
            else r=mid-1;
        }
          
        printf("%d\n",ans);
    }
    return 0;
}

 




于是手写一个

70ms+

#include
using namespace std;

typedef unsigned long long ull;
const ull base=123;
const int maxn=4e5+10;
const int hashh= 1e6+10;

char s[511][511];
ull num[511*511],f[511][511];
int n,m; 
struct hashmap
{
	ull a[maxn];
	int head[hashh];
	int nxt[maxn];
	int siz;
	void init()
	{
		memset(head,-1,sizeof head);
		siz = 0;
	}
	bool find(ull val)
	{
		int tmp = (val % hashh + hashh) % hashh;
		for(int i = head[tmp];i!=-1;i = nxt[i])
		{
			if(val == a[i]) return true;
		}
		return false;
	}
	void add(ull val)
	{
		int tmp = (val % hashh + hashh) % hashh;
		if(find(val)) return ;
		a[siz] = val;
		nxt[siz] = head[tmp];//令next指向-1;
		head[tmp] = siz++; 
	}
}Hash;

void init()
{
	memset(f,0,sizeof f);
	num[0]=1;
	int cnt = 0;
	for(int i = 1; i <= n;i++)
	 for(int j = 1;j <= m;j++)
	 {
	 	cnt++;
	 	num[cnt] = num[cnt-1]*base;
	 	f[i][j] = f[i-1][j] + f[i][j-1] -f[i-1][j-1] + (s[i][j]-'0')*num[cnt];
	 }
	 return ;
}
bool check(int x)
{
	Hash.init();
	for(int i = x;i <= n;i++)
	 for(int j = x;j <= m;j++)
	 {
	 	ull tmp = f[i][j] - f[i][j - x] - f[i - x][j] + f[i - x][j - x];
	 	tmp = tmp * num[(n - i) * m + m - j];
	 	if(Hash.find(tmp) == true)
	 	return true;
	 	Hash.add(tmp);
	 }
	 return false;
}
int main()
{
	while(~scanf("%d %d",&n,&m))
	{
		
		for(int i = 1;i <= n;i++) scanf("%s",s[i]+1);
		init();
		int l = 1,r=min(n,m),mid,ans = -1;
		while(l<=r)
		{
			mid = (l + r) >> 1;
			if(check(mid))
			l = mid + 1,ans = mid;
			else
			r = mid - 1;
		}
		printf("%d\n",ans);
	}
	return 0;
}


你可能感兴趣的:(hash)