二维哈希

Author Avatar
Axell 2月 10, 2019
  • 在其它设备中阅读本文章

二维哈希

要求在预处理后,能用O(1)的时间得到矩阵内任何矩形区域的哈希值
例题goto

思路

类似于二维前缀和,预处理出F[i,j]表示前1-i行i-j列展开的哈希值,预处理时,建议用两个不同的质数作为权值,一个处理行上的,一个处理列上的,两个过程可以分开进行

ULL hasha[n+1][m+1];
for (int i=1;i<=n;++i){
    for (int j=1;j<=m;++j){
        hasha[i][j]+=hasha[i][j-1]*pi;
    }
}
for (int i=1;i<=n;++i){
    for (int j=1;j<=m;++j){
        hasha[i][j]+=hasha[i-1][j]*pj;
    }
}

想要获得其中一块区域的哈希值时,用类似于二维前缀和的方式,下面是获取以i,j为右下角,长宽分别为a,b的区域的哈希值

p1[1]=pi;
p2[1]=pj;
for (int i=2;i<=a;++i) p2[i]=p2[i-1]*pj;
for (int i=2;i<=b;++i) p1[i]=p1[i-1]*pi;

ULL tmp=hasha[i][j]-hasha[i-a][j]*p2[a]-hasha[i][j-b]*p1[b]+hasha[i-a][j-b]*p2[a]*p1[b];  //注意P1,P2不要搞错

下面是原题代码,无奈开O3优化

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
#pragma GCC optimize (3)
#pragma G++ optimize (3)
ULL pi=131,pj=13331,p1[1005],p2[1005],hasha[1005][1005],hashb[1005][1005],ans[1000005],cnt;
int n,m,a,b;

void init(){
    p1[1]=pi;
    p2[1]=pj;
    for (int i=2;i<=a;++i) p2[i]=p2[i-1]*pj;
    for (int i=2;i<=b;++i) p1[i]=p1[i-1]*pi;
}

int main(){
    cin>>n>>m>>a>>b;
    init();
    for (int i=1;i<=n;++i){
        for (int j=1;j<=m;++j){
            scanf("%1llu",&hasha[i][j]);
        }
    }
    for (int i=1;i<=n;++i){
        for (int j=1;j<=m;++j){
            hasha[i][j]+=hasha[i][j-1]*pi;
        }
    }
    for (int i=1;i<=n;++i){
        for (int j=1;j<=m;++j){
            hasha[i][j]+=hasha[i-1][j]*pj;
        }
    }
    for (int i=a;i<=n;++i){
        for (int j=b;j<=m;++j){
            ULL tmp=hasha[i][j];
            tmp-=hasha[i-a][j]*p2[a];
            tmp-=hasha[i][j-b]*p1[b];
            tmp+=hasha[i-a][j-b]*p2[a]*p1[b];
            ans[++cnt]=tmp;
        }
    }
    int t;
    cin>>t;
    while (t--){
        for (int i=1;i<=a;++i){
            for (int j=1;j<=b;++j){
                scanf("%1llu",&hashb[i][j]);
            }
        }
        for (int i=1;i<=a;++i){
            for (int j=1;j<=b;++j){
                hashb[i][j]+=hashb[i][j-1]*pi;
            }
        }
        for (int i=1;i<=a;++i){
            for (int j=1;j<=b;++j){
                hashb[i][j]+=hashb[i-1][j]*pj;
            }
        }
        int y=0;
        ULL tmp=hashb[a][b];
        for (int i=1;i<=cnt;++i){
            if (ans[i]==tmp){
                puts("1");
                y=1;
                break;
            }
        }
        if (!y) puts("0");
    }
    return 0;
}

知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 未本地化版本许可协议进行许可。

本文链接:https://hs-blog.axell.top/archives/98/