二维哈希讲解-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

相关文章

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

Python基础文件操作方法超详细讲解(详解版)

《Python基础文件操作方法超详细讲解(详解版)》文件就是操作系统为用户或应用程序提供的一个读写硬盘的虚拟单位,文件的核心操作就是读和写,:本文主要介绍Python基础文件操作方法超详细讲解的相... 目录一、文件操作1. 文件打开与关闭1.1 打开文件1.2 关闭文件2. 访问模式及说明二、文件读写1.

C# WinForms存储过程操作数据库的实例讲解

《C#WinForms存储过程操作数据库的实例讲解》:本文主要介绍C#WinForms存储过程操作数据库的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、存储过程基础二、C# 调用流程1. 数据库连接配置2. 执行存储过程(增删改)3. 查询数据三、事务处

C++快速排序超详细讲解

《C++快速排序超详细讲解》快速排序是一种高效的排序算法,通过分治法将数组划分为两部分,递归排序,直到整个数组有序,通过代码解析和示例,详细解释了快速排序的工作原理和实现过程,需要的朋友可以参考下... 目录一、快速排序原理二、快速排序标准代码三、代码解析四、使用while循环的快速排序1.代码代码1.由快

CSS3 最强二维布局系统之Grid 网格布局

《CSS3最强二维布局系统之Grid网格布局》CS3的Grid网格布局是目前最强的二维布局系统,可以同时对列和行进行处理,将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局,本文介... 深入学习 css3 目前最强大的布局系统 Grid 网格布局Grid 网格布局的基本认识Grid 网

Java集合中的List超详细讲解

《Java集合中的List超详细讲解》本文详细介绍了Java集合框架中的List接口,包括其在集合中的位置、继承体系、常用操作和代码示例,以及不同实现类(如ArrayList、LinkedList和V... 目录一,List的继承体系二,List的常用操作及代码示例1,创建List实例2,增加元素3,访问元

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

Python itertools中accumulate函数用法及使用运用详细讲解

《Pythonitertools中accumulate函数用法及使用运用详细讲解》:本文主要介绍Python的itertools库中的accumulate函数,该函数可以计算累积和或通过指定函数... 目录1.1前言:1.2定义:1.3衍生用法:1.3Leetcode的实际运用:总结 1.1前言:本文将详

Redis的Zset类型及相关命令详细讲解

《Redis的Zset类型及相关命令详细讲解》:本文主要介绍Redis的Zset类型及相关命令的相关资料,有序集合Zset是一种Redis数据结构,它类似于集合Set,但每个元素都有一个关联的分数... 目录Zset简介ZADDZCARDZCOUNTZRANGEZREVRANGEZRANGEBYSCOREZ

Go中sync.Once源码的深度讲解

《Go中sync.Once源码的深度讲解》sync.Once是Go语言标准库中的一个同步原语,用于确保某个操作只执行一次,本文将从源码出发为大家详细介绍一下sync.Once的具体使用,x希望对大家有... 目录概念简单示例源码解读总结概念sync.Once是Go语言标准库中的一个同步原语,用于确保某个操