题目链接:矩阵
题意:给定一个$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\n"); else printf("0\n"); } return 0; }