AcWing

题目链接:矩阵

题意:给定一个$m$行$n$列的$01$矩阵$($只包含数字$0$或$1$的矩阵$)$,再执行$q$次询问,每次询问给出一个$a$行$b$列的$01$矩阵,求该矩阵是否在原矩阵中出现过

思路:二维哈希,从矩阵的右下角为低位到矩阵的左上角为高位,先求出每一行的一维哈希值$h[i][j]$,在$a$行$b$列的$01$矩阵向下移动的过程中,先向下扩展成$a+1$行$b$列的$01$矩阵,再将最上面的一行减去,这样矩阵就会向下移动一格,设原来$a$行$b$列矩阵的哈希值为$t$,所以新矩阵的哈希值

$$res=t*p[b]+(h[i+1][j]-h[i+1][j-b]*p[b])-((h[i-a][j]-h[i-a][j-b]*p[b])*p[a*b])$$

用$set$存储每个子矩阵的哈希值,求出询问矩阵的哈希值,判断是否出现过

其他二维哈希的题目,令矩阵的右下角为低位、矩阵的左上角为高位,然后推一下公式即可。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <set>

using namespace std;

typedef unsigned long long ull;

const int N = 1010;
const int M = N * N;
const ull P = 131;

int n, m, a, b, k;
ull h[N][N], p[M];

ull Hash(ull f[], int l, int r)
{
    return f[r] - f[l - 1] * p[r - l + 1];
}

int main()
{
    scanf("%d%d%d%d", &n, &m, &a, &b);
    p[0] = 1;
    for (int i = 1; i <= n * m; i++)
        p[i] = p[i - 1] * P;
    for (int i = 1; i <= n; i++) {
        char s[N];
        scanf("%s", s + 1);
        for (int j = 1; j <= m; j++)
            h[i][j] = h[i][j - 1] * P + s[j] - '0';
    }
    set<ull> st;
    for (int i = b; i <= m; i++) {
        ull t = 0;
        int l = i - b + 1, r = i;
        for (int j = 1; j <= n; j++) {
            t = t * p[b] + Hash(h[j], l, r);
            if (j > a) t -= Hash(h[j - a], l, r) * p[a * b];
            if (j >= a) st.insert(t);
        }
    }
    scanf("%d", &k);
    while (k--) {
        ull t = 0;    
        char s[N];
        for (int i = 1; i <= a; i++) {
            scanf("%s", s + 1);
            for (int j = 1; j <= b; j++)
                t = t * P + s[j] - '0';
        }
        if (st.count(t)) printf("1
");
        else printf("0
");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/zzzzzzy/p/12250473.html