牛客 矩阵 二维哈希+二分

题目链接

题意

给出一个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;
}

猜你喜欢

转载自blog.csdn.net/c___c18/article/details/115545503