二维哈希讲解-And-AcWing-156. 矩阵-题解

2024-04-11 11:08

本文主要是介绍二维哈希讲解-And-AcWing-156. 矩阵-题解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

如果还不清楚什么是哈希算法,可以点击这里。

文章目录

  • 二维哈希思想
  • AcWing-156. 矩阵
      • Description
      • Input
      • Output
      • Sample Input
      • Sample Output
    • 题目大意
    • 解题思路
    • AC代码

二维哈希思想

说一下我对二维哈希算法的理解

之前,我们已经可以把一维数组映射成一个数了。如下:

数组名元素哈希值
A1,5,6,9,8,4,0,5,6,8,4,5,6,9,8,7,415698405684569874
B5,9,8,4,7,0,2,5,6,9,4,5,6,9,8,4,559847025694569845
C1,5,6,9,8,4,0,5,6,8,4,5,6,9,8,7,415698405684569874

现在,我们来把一个二维数组映射成一个数。
为了便于理解,我们仍先用10进制,把较少的数据映射成一个数。

数组名元素
A7 9 3
1 6 5
5 7 2
B4 3
5 0

二维数组有什么,视为一维数组就好了。
可以映射为:

数组名元素哈希值
A7 9 3
1 6 5
5 7 2
793165572
B4 3
5 0
4350

方法也不难

//假设是n行m列的矩阵A
typedef unsigned long long ull;
ull Hash()
{ull ans=0;int p=10;for(int i=0;i<n;i++){for(int j=0;j<m;j++){ans=ans*p+A[i][j];}}return ans;
}

仅仅知道这些还不够,我们还要知道如何快速求得大矩阵A中一个固定大小的小矩阵的哈希值:

数组名元素
A7 9 3
1 6 5
5 7 2

现在,让你算出A中所有大小为 2 ∗ 2 2*2 22的子矩阵的哈希值:
A中有4个大小为 2 ∗ 2 2*2 22的子矩阵,分别为:

方位元素
原矩阵7 9 3
1 6 5
5 7 2
左上角7 9
1 6
右上角9 3
6 5
左下角1 6
5 7
左上角6 5
7 2

通过常规方法,我们可以计算出这4个子矩阵的哈希值:

方位元素计算过程哈希值
左上角7 9
1 6
7+9*10+1*10*10+6*10*10*107916
右上角9 3
6 5
9+3*10+6*10*10+5*10*10*109365
左下角1 6
5 7
1+6*10+5*10*10+7*10*10*101657
左上角6 5
7 2
6+5*10+7*10*10+2*10*10*106572

为什么有删除线?
是因为这样计算太慢了。

这是要找 2 ∗ 2 2*2 22的子矩阵,每次计算需要 2 ∗ 2 = 4 2*2=4 22=4次。( n ∗ m n*m nm的子矩阵每次要计算 n ∗ m n*m nm次)。如果 n n n m m m大一些呢?就会很慢了。


左上角和左下角的子矩阵,有一行是重复的。
所以能不能根据上一个子矩阵,快速( O ( 1 ) O(1) O(1))求得新的子矩阵呢?

比较左上角和左下角的子矩阵,它们有共同的一行: 1 6 1\ \ 6 1  6。它们的哈希值里也都有相邻的两个数 16 16 16

方位元素哈希值
左上角7 9
1 6
7916
左下角1 6
5 7
1657

那么怎么由左上角的 7916 7916 7916直接快速得到左下角的 1657 1657 1657呢?
7916 ∗ 1 0 2 + 57 − 79 ∗ 1 0 2 ∗ 2 = 791600 + 57 − 790000 = 1657 7916*10^2+57-79*10^{2*2}=791600+57-790000=1657 7916102+57791022=791600+57790000=1657
其中,加上的 57 57 57是左下角比左上角多出的新的一行的 5 7 5\ \ 7 5  7的哈希值;
减去的 79 79 79是左下角没有而在左上角出现过的一行 7 9 7\ \ 9 7  9的哈希值。


而一行中某段的哈希值如何快速求得呢?

对于一行中的某些连续的值,用a[i]代表前i个数的哈希值,那么从l到r的这些数的哈希值就是 a [ r ] − a [ l − 1 ] ∗ p r − l + 1 a[r]-a[l-1]*p^{r-l+1} a[r]a[l1]prl+1
例如:一行有4个元素: 4 5 8 3 4\ 5\ 8\ 3 4 5 8 3,先预处理得到 a [ ] : { 4 , 45 , 458 , 4583 } a[]:\{4,45,458,4583\} a[]:{4,45,458,4583}
现在突然让你求第二个元素到第三个元素的哈希( 58 58 58),公式为: 458 − 4 ∗ 1 0 2 = 58 458-4*10^{2}=58 4584102=58 l = 2 , r = 3 l=2,r=3 l=2,r=3)。

所以,给定大矩阵A后,我们先对其每一行进行预处理,求出每一行的前i个元素的哈希。
之后求所有的大小为 a ∗ b a*b ab的矩阵,两层循环:第一层是列,第二层是行。
对于第一层的这一列,下面子矩阵的哈希能够根据上面子矩阵的哈希快速求得。


又双叒叕认为讲得差不多了,不如根据一道题来实战一下:

AcWing-156. 矩阵

Time Limit: 2s
Memory Limit: 64M
传送门

Description

给定一个 M M M N N N 列的 01 01 01 矩阵(只包含数字 0 0 0 1 1 1 的矩阵),再执行 Q Q Q 次询问,每次询问给出一个 A A A B B B 列的 01 01 01 矩阵,求该矩阵是否在原矩阵中出现过。

Input

第一行四个整数 M , N , A , B M,N,A,B M,N,A,B

接下来一个 M M M N N N 列的 01 01 01 矩阵,数字之间没有空格。

接下来一个整数 Q Q Q

接下来 Q Q Q A A A B B B 列的 01 01 01 矩阵,数字之间没有空格。

A ≤ 100 , M , N , B ≤ 1000 , Q ≤ 1000 A≤100 ,M,N,B≤1000,Q≤1000 A100M,N,B1000Q1000

Output

对于每个询问,输出 1 表示出现过,0 表示没有出现过。

Sample Input

3 3 2 2
111
000
111
3
11
00
11
11
00
11

Sample Output

1
0
1

题目大意

给你一个大矩阵,之后给你一些小矩阵,问小矩阵在大矩阵中有没有出现过。


解题思路

因为小矩阵的大小是确定的,所以我们先把大矩阵的每个大小为小矩阵的子矩阵的哈希值计算出来,并保存。
之后没输入一个小矩阵,就计算出它的哈希值,看看有没有在大矩阵的子矩阵中出现过。

之后的看看注释理解理解吧

AC代码

#include<bits/stdc++.h>
#include<set>using namespace std;
typedef unsigned long long ull;
const int p=131;//131进制
ull ma[1010][1010];//ma[i][j]代表大矩阵第i行的前j个元素的哈希值
ull power[1000010];//power[i]代表131的i次方
ull getLine(int x,int l,int r)//第x行从l到r的hash值
{return ma[x][r]-ma[x][l-1]*power[r-l+1];//这个公式在上文有推导过
}
int main()
{int a,b,n,m;cin>>m>>n>>a>>b;//大矩阵和小矩阵的数据量power[0]=1;//131的0次幂是1for(int i=1;i<=n*m;i++)power[i]=power[i-1]*p;//计算出次幂for(int i=1;i<=n;i++)//有前缀一般从小标1开始{string s;cin>>s;//输入这一行for(int j=0;j<m;j++)ma[i][j+1]=ma[i][j]*p+s[j]-'0';//次公式上文有推导过,前面的*进制+这个}set<ull>S;//用来记录一个哈希值是否出现过for(int i=b;i<=m;i++)//列:b列到m列;i枚举的子矩阵的最右边的一列{ull s=0;//初始值是0int l=i-b+1,r=i;//列的左边是l,右边是r;r就是i,一行有b个,所以l=i-b+1for(int j=1;j<=n;j++)//j是行,大矩阵的从第1行到最后一行{s=s*power[b]+getLine(j,l,r);//加上得到这一行从l到r的哈希if(j-a>0)s-=getLine(j-a,l,r)*power[a*b];//如果这不是前几行,而是已经>a+1行了,因为只求需要a行的子矩阵的哈希,所以就需要把多余a行的上一行减掉if(j-a>=0)S.insert(s);//如果已经有a行了,就是一个合格大小的子矩阵,记录下来}}int q;cin>>q;//q个小矩阵while(q--){ull s=0;//对每个小矩阵的哈希,初始值是0string str;for(int i=0;i<a;i++)//每一行{cin>>str;//输入这一行for(int j=0;j<b;j++)//这一行的每个元素{s=s*p+str[j]-'0';//新哈希值=老哈希值*进制+这个元素对应的值}}puts(S.count(s)?"1":"0");//S中有s吗 ?如果有输出1 :否则输出0}return 0;
}

掌握得怎么样,还想再练练吗?


原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/116563356

这篇关于二维哈希讲解-And-AcWing-156. 矩阵-题解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/893907

相关文章

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

poj2576(二维背包)

题意:n个人分成两组,两组人数只差小于1 , 并且体重只差最小 对于人数要求恰好装满,对于体重要求尽量多,一开始没做出来,看了下解题,按照自己的感觉写,然后a了 状态转移方程:dp[i][j] = max(dp[i][j],dp[i-1][j-c[k]]+c[k]);其中i表示人数,j表示背包容量,k表示输入的体重的 代码如下: #include<iostream>#include<

hdu2159(二维背包)

这是我的第一道二维背包题,没想到自己一下子就A了,但是代码写的比较乱,下面的代码是我有重新修改的 状态转移:dp[i][j] = max(dp[i][j], dp[i-1][j-c[z]]+v[z]); 其中dp[i][j]表示,打了i个怪物,消耗j的耐力值,所得到的最大经验值 代码如下: #include<iostream>#include<algorithm>#include<

usaco 1.3 Prime Cryptarithm(简单哈希表暴搜剪枝)

思路: 1. 用一个 hash[ ] 数组存放输入的数字,令 hash[ tmp ]=1 。 2. 一个自定义函数 check( ) ,检查各位是否为输入的数字。 3. 暴搜。第一行数从 100到999,第二行数从 10到99。 4. 剪枝。 代码: /*ID: who jayLANG: C++TASK: crypt1*/#include<stdio.h>bool h

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

hdu 4565 推倒公式+矩阵快速幂

题意 求下式的值: Sn=⌈ (a+b√)n⌉%m S_n = \lceil\ (a + \sqrt{b}) ^ n \rceil\% m 其中: 0<a,m<215 0< a, m < 2^{15} 0<b,n<231 0 < b, n < 2^{31} (a−1)2<b<a2 (a-1)^2< b < a^2 解析 令: An=(a+b√)n A_n = (a +

HDU 2159 二维完全背包

FATE 最近xhd正在玩一款叫做FATE的游戏,为了得到极品装备,xhd在不停的杀怪做任务。久而久之xhd开始对杀怪产生的厌恶感,但又不得不通过杀怪来升完这最后一级。现在的问题是,xhd升掉最后一级还需n的经验值,xhd还留有m的忍耐度,每杀一个怪xhd会得到相应的经验,并减掉相应的忍耐度。当忍耐度降到0或者0以下时,xhd就不会玩这游戏。xhd还说了他最多只杀s只怪。请问他能

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

hdu 6198 dfs枚举找规律+矩阵乘法

number number number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Problem Description We define a sequence  F : ⋅   F0=0,F1=1 ; ⋅   Fn=Fn