作用是将很大的值映射到一个很小的值
将一个很大的值映射到数组下标范围之内
k=(x%N+N)%N
将一个较大值x映射成一个较小值k,一般的N是一个很大的质数,为了避免产生不同较大值转换成相同较小值的冲突。
对于解决冲突主要分为两个方法:拉链法和开放寻址法
拉链法
就是在发生冲突的位置开设单链表,将在h[k]
发生冲突的元素统一存放到由h[k]
开出来的单链表中来。
开放寻址法
不用设置单链表来解决冲突,就是在h[k]
发生冲突的时候,就往后面寻找有没有空的位置进行存储。
#include
#include
#include
using namespace std;
const int N=100003;//一般取100003就够用了
int h[N],e[N],ne[N],idx;//h[]表示哈希的数组,e[],ne[]表示从哈希表每个单元素拉出来的单链表,idx表示单链表节点
void insert(int x)
{
int k=(x%N+N)%N;//映射
e[idx]=x;//单链表的头插操作
ne[idx]=h[k];
h[k]=idx++;
}
bool find(int x)
{
int k=(x%N+N)%N;//映射
for(int i=h[k];i!=-1;i=ne[i])//单链表的查找操作
{
if(e[i]==x)return true;
}
return false;
}
int main()
{
int n;
cin>>n;
memset(h, -1, sizeof h);//初始值为-1,表示每个单链表为空的
while (n -- )
{
char op[2];
int x;
cin>>op>>x;
if(op[0]=='I')
{
insert(x);
}
else
{
if(find(x)) puts("Yes");
else puts("No");
}
}
}
当存的节点出现冲突的时候,就将这个节点存到hash表的链表中
#include
#include
#include
using namespace std;
const int N=200003,null=0x3f3f3f3f;//N表示进行映射的大数,null是一个标志,h[i]==null表示哈希表这个位置是空的
int h[N];
int find(int x)
{
int k=(x%N+N)%N;
while(h[k]!=null&&h[k]!=x)//开始找空位
{
k++;
if(k==N)k=0;
}
return k;
}
int main()
{
int n;
cin>>n;
memset(h,0x3f3f,sizeof h);//一开始哈希表所有位置都是空的
while (n -- )
{
char op[2];
int x;
cin>>op>>x;
if(op[0]=='I')//插入操作
{
int k=find(x);
h[k]=x;
}
else
{
if(h[find(x)]!=null)puts("Yes");//查询操作
else puts("No");
}
}
}
开放寻址法,当存在冲突的时候,就向这个点后面遍历找到一个空的位置进行插入数值操作
字符串哈希
给定一个长度为 n n n 的字符串,再给定 m m m 个询问,每个询问包含四个整数 l 1 , r 1 , l 2 , r 2 l_1, r_1, l_2, r_2 l1,r1,l2,r2,请你判断 [ l 1 , r 1 ] [l_1, r_1] [l1,r1] 和 [ l 2 , r 2 ] [l_2, r_2] [l2,r2] 这两个区间所包含的字符串子串是否完全相同。
字符串中只包含大小写英文字母和数字。
输入格式
第一行包含整数 n n n 和 m m m,表示字符串长度和询问次数。
第二行包含一个长度为 n n n 的字符串,字符串中只包含大小写英文字母和数字。
接下来 m m m 行,每行包含四个整数 l 1 , r 1 , l 2 , r 2 l_1, r_1, l_2, r_2 l1,r1,l2,r2,表示一次询问所涉及的两个区间。
注意,字符串的位置从 1 1 1 开始编号。
输出格式
对于每个询问输出一个结果,如果两个字符串子串完全相同则输出
Yes
,否则输出No
。每个结果占一行。
数据范围
1 ≤ n , m ≤ 1 0 5 1 \le n, m \le 10^5 1≤n,m≤105
输入样例:
8 3 aabbaabb 1 3 5 7 1 3 6 8 1 2 1 2
输出样例:
Yes No Yes
我们将一个字符串想象成一个数字
例如:ABCD 我们可以看作 1234
然后我们将这个数字转化成p进制的数就是 1 ∗ p 3 + 2 ∗ p 2 + 3 ∗ p 1 + 4 ∗ p 0 1*p^3+2*p^{2}+3*p^{1}+4*p^0 1∗p3+2∗p2+3∗p1+4∗p0
一般的 p取131或者13331
由于这个数字会很大,我们需要进行取模,模上一个大数Q ,Q一般取 2 64 2^{64} 264
但是我们将这个数存在unsigned long long 类型的数组中,当数字大于 2 64 2^{64} 264时就会自动帮你取模(也就是溢出导致取模)
整体映射流程:
字符串str=abcd...
—>大数num=12345..
—>k=num%Q
这样我们就得到了一段字符串对应的哈希值
然后我们需要把字符串的的前缀和记录下来
当我们已经知道[0, l-1] ,[0, r]
的字符串表示的值的时候,我们可以根据公式
h [ l , r ] = h [ r ] − h [ l − 1 ] ∗ p r − l + 1 h[l,r]=h[r]-h[l-1]*p^{r-l+1} h[l,r]=h[r]−h[l−1]∗pr−l+1得到[l,r]
这段区间的字符串的哈希值
然后通过比较字符串的哈希值来判断这两个字符串是不是相同的
#include
#include
using namespace std;
const int N=100010,P=131;
typedef unsigned long long ULL;//数据存在unsigned long long 类型的数组中,当数字大于2^64时就会自动帮你取模
ULL h[N],p[N];
char str[N];
int query(int l,int r)
{
return h[r]-h[l-1]*p[r-l+1];//区间[l,r]字符串对应的哈希值
}
int main()
{
int n,m;
cin>>n>>m;
cin>>str+1;
p[0]=1;
for(int i=1;i<=n;i++)
{
p[i]=p[i-1]*P;//因为query中要用到p^{r-l+1}因此不妨将p的各种幂存下来
h[i]=h[i-1]*P+str[i];//这里字符串每个字符的值是直接用ascal码了
}
while(m--)
{
int l1,r1,l2,r2;
cin>>l1>>r1>>l2>>r2;
if(query(l1,r1)==query(l2,r2))puts("Yes");
else puts("No");
}
return 0;
}
//初始化
vector<int> q;
vector<int> q(n,value);//创建n个值为value的元素
//倍增的思想,数组长度不够就创建一个新的2倍长度的数组,然后复制粘贴过去
//支持的方法
size()
empty()
clear()
front()/back()
push_back()/pop_back()
begin()/end()
[]
支持比较运算(两个vector进行比较),按照字典序
pair<int,int>
first
second
支持比较运算,first作为第一关键字,second作为第二个关键字,按照字典序
//字符串
//常用方法
size()
empty()
clear()
substr(字符串起始下标,字符串长度)//查询子串
//常用方法
size()
empty()
push()
pop()
front()
back()
//注意没有clear函数
//优先队列,堆,默认是大根堆
push()//插入元素
top()//返回顶端元素
pop()//弹出顶端元素
priority_queue<int,vector<int>,greater<int>> heap//定义一个小根堆
//常用方法
push()
top()
pop()
size()
empty()
//双端队列
size()
empty()
clear()
front()/back()
push_back()/pop_back()
push_front()/pop_front()
begin()/end()
[]
size()
empty()
clear()
set/multiset:
set中没有重复元素/multiset支持重复元素
insert()
find()
count()
erase()
1.如果输入的是一个数x,就删除所有x
2.如果输入的是一个迭代器,删除的就是这个迭代器o
lower_bound()/upper_bound()
louwer_bound(x) 返回大于等于x的最小的数的迭代器
upper_bound(x) 返回大于x的最小的数的迭代器
map/multimap
map中没有重复元素/multimap支持重复元素
insert() //插入pair类型
find()
count()
erase()//删除pair类型,或者迭代器
[] //时间复杂度是O(logn)
louwer_bound(x)
upper_bound(x)
和set/multiset/map/multimap类似,只是增删改查的时间复杂度是O(1)
不支持lower_bound()/upper_bound(),也不支持迭代器自增自减
没用到过,用到了再说…