欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

牛客 矩阵 二维哈希+二分

程序员文章站 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;
}