作用是将很大的值映射到一个很小的值
将一个很大的值映射到数组下标范围之内
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]这段区间的字符串的哈希值
然后通过比较字符串的哈希值来判断这两个字符串是不是相同的
#includeSTL#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 动态变长的数组
- string 字符串
- queue 单向队列
- priority_queue优先队列(堆)
- stack 栈
- deque 双端队列()
- set,map,multi_set,multi_map (红黑树)
- unordered_map,unordered_set
- bitset
//初始化 vectorpairq; vector q(n,value);//创建n个值为value的元素 //倍增的思想,数组长度不够就创建一个新的2倍长度的数组,然后复制粘贴过去 //支持的方法 size() empty() clear() front()/back() push_back()/pop_back() begin()/end() [] 支持比较运算(两个vector进行比较),按照字典序
pairstringfirst second 支持比较运算,first作为第一关键字,second作为第二个关键字,按照字典序
//字符串 //常用方法 size() empty() clear() substr(字符串起始下标,字符串长度)//查询子串queue
//常用方法 size() empty() push() pop() front() back() //注意没有clear函数priority_queue
//优先队列,堆,默认是大根堆 push()//插入元素 top()//返回顶端元素 pop()//弹出顶端元素 priority_queuestack,greater > heap//定义一个小根堆
//常用方法
push()
top()
pop()
size()
empty()
deque
//双端队列
size()
empty()
clear()
front()/back()
push_back()/pop_back()
push_front()/pop_front()
begin()/end()
[]
set/multiset/map/multimap
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)
unordered_map/unordered_multimap/unordered_set/unordered_multiset(哈希表)
和set/multiset/map/multimap类似,只是增删改查的时间复杂度是O(1) 不支持lower_bound()/upper_bound(),也不支持迭代器自增自减bitset(只能存储二进制数的数组)
没用到过,用到了再说…
暑期编程PK赛 得CSDN机械键盘等精美礼品!


