哈希表
例题:
拉链法
模拟散列表
维护一个集合,支持如下几种操作:
I x
,插入一个数 xx;Q x
,询问数 xx 是否在集合中出现过;
现在要进行 N 次操作,对于每个询问操作输出对应的结果。
输入格式
第一行包含整数 N,表示操作数量。
接下来 N 行,每行包含一个操作指令,操作指令为 I x
,Q x
中的一种。
输出格式
对于每个询问指令 Q x
,输出一个询问结果,如果 xx 在集合中出现过,则输出 Yes
,否则输出 No
。
每个结果占一行。
数据范围
1≤N≤105
−109≤x≤109
输入样例:
5
I 1
I 2
I 3
Q 2
Q 5
输出样例:
Yes
No
解题代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N=100010;
int h[N],e[N],ne[N],idx;//e存的链表的值,ne存的下一个节点的下标(next)
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])//h[k]存的是头节点指向的下一节点的下标
if(e[i] == x)
return true;
return false;
}
int main()
{
/* for(int i=100000;;i ++) //找到大于十万的第一个质数
{
bool flag = true;
for(int j=2;j*j<=i;j++)
if(i%j == 0)
{
flag = false;
break;
}
if(flag)
{
cout << i<<endl;
break;
}
}*/
int n;
scanf("%d",&n);
memset(h,-1,sizeof h);//清空所有的槽
while(n --)
{
char op[2];
int x;
scanf("%s%d",op,&x);
if(*op == 'I') insert(x);
else
{
if(find(x)) puts("Yes");
else puts("No");
}
}
return 0;
}
开放寻址法
解题代码:
#include <cstring>
#include <iostream>
using namespace std;
const int N = 200003, null = 0x3f3f3f3f;
int h[N];
int find(int x)
{
int t = (x % N + N) % N;
while (h[t] != null && h[t] != x)//坑有人,并且不是x
{
t ++ ;
if (t == N) t = 0;//坑位看完了,看第一个
}
return t;
}
int main()
{
memset(h, 0x3f, sizeof h);//清空所有的槽
int n;
scanf("%d", &n);
while (n -- )
{
char op[2];
int x;
scanf("%s%d", op, &x);
if (*op == 'I') h[find(x)] = x;
else
{
if (h[find(x)] == null) puts("No");
else puts("Yes");
}
}
return 0;
}
字符串哈希方式
例题:
字符串哈希
给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l1,r1,l2,r2请你判断 [l1,r1] 和 [l2,r2] 这两个区间所包含的字符串子串是否完全相同。
字符串中只包含大小写英文字母和数字。
输入格式
第一行包含整数 n 和 m,表示字符串长度和询问次数。
第二行包含一个长度为 n 的字符串,字符串中只包含大小写英文字母和数字。
接下来 m 行,每行包含四个整数 l1,r1,l2,r2表示一次询问所涉及的两个区间。
注意,字符串的位置从 1 开始编号。
输出格式
对于每个询问输出一个结果,如果两个字符串子串完全相同则输出 Yes
,否则输出 No
。
每个结果占一行。
数据范围
1≤n,m≤105
输入样例:
8 3
aabbaabb
1 3 5 7
1 3 6 8
1 2 1 2
输出样例:
Yes
No
Yes
解题:
#include <iostream>
using namespace std;
typedef unsigned long long ULL;
const int N =100010,P=131;
int n,m;
char str[N];
ULL h[N],p[N];//p放的是多少次方
ULL get(int l,int r)
{
return h[r]-h[l-1]*p[r-l+1];
}
int main()
{
scanf("%d%d%s",&n,&m,str +1);
p[0] =1;
for(int i=1;i<=n;i++)
{
p[i] =p[i-1]*P;
h[i]=h[i-1]*P+str[i];
}
while(m --)
{
int l1,r1,l2,r2;
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
if(get(l1,r1) == get(l2,r2)) puts("Yes");
else puts("No");
}
return 0;
}
STL
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
vector,变长数组,倍增的思想
size() 返回元素个数
empty() 返回是否为空
clear() 清空
front()/back() 返回vecto第一个数/返回vector最后一个数
push_back()/pop_back() 向vector最后插入一个数/删除最后一个数
begin()/end() begin是第0个数/end是最后一个数的后面一个
支持随机选举
for(int i =0;i<10;i++) a.push_back(i);
for(int i=0;i<a.size();i++) cout << a[i]<<' ';
for(vector<int>::iterator i=a.begin();i!=a.end();i++) cout <<*i <<' ';
//a.begin =a[0] a.end=a[size]
cout << endl;
for(auto x:a) cout << x <<' ';
cout <<endl;
支持比较运算(按字典比较)
vector<int> a(4,3),b(3,4);
if(a<b) puts("a<b");
pair<int ,int> pair<int ,string>p;
first,第一个元素 p.first;
second,第二个元素 p.second;
支持比较运算,以first为第一关键字,以second为第二关键字(字典序)
pair<int,pair<int,int>>p;
p=make_pair(10,"acj");
p={20,"abc"};
string,字符串,substr(),c_str()
size()/length() 返回元素个数/返回字符串长度
empty() 返回是否为空
clear() 清空
可以直接添加字符串
string a ="adwd";
a+=“ddef”;
a+='c'; cout <<a <<endl;
substr(1,2):从下标1开始,长度是2,超出长度,输出最后一个为止
c_str():存储字符串的起始位置的地址
queue,队列
size() 返回元素个数
empty() 返回是否为空
push():向队尾插入一个元素
front():返回队头元素
back():返回队尾元素
pop():弹出队头元素
priority_queue,优先队列,默认大根堆
push():插入一个元素
top():返回堆顶元素
pop():弹出堆顶元素
//小根堆
priority_queue<int,vector<int>,greater<int>> heap;
or
priority_queue<int> heap;
heap.push(-x);
stack,栈
size() 返回元素个数
empty() 返回是否为空
push():向栈顶插入一个元素
top():返回栈顶元素
pop():弹出栈顶元素
deque,双端队列
size() 返回元素个数
empty() 返回是否为空
clear() 清空
front():返回第一个元素
back():返回最后一个元素
push_back()/pop_back():向最后插入一个元素/弹出最后一个元素
push_front()/pop_front():向队首插入一个元素/弹出队首元素
begin()/end() begin是第0个数/end是最后一个数的后面一个
set,map,multiset,multimap,基于平衡二叉树(红黑树),动态维护有序序列
size() 返回元素个数
empty() 返回是否为空
clear() 清空
begin()/end() ++,-- 返回前驱和后继
set/multiset
insert() 插入一个数
find() 查找一个数
count() 返回某一个数的个数
erase()
(1)输入是一个数x,删除所有x
(2)输入一个迭代器,删除这个迭代器
lower_bound()/uper_bound()
lower_bound():返回大于等于x的最小的数的迭代器
uper_bound():返回大于x的最小的数的迭代器
set<int> s;//不能有重复元素
multiset<int> ms;//可有重复的
map/multimap
insert() 插入的数是一个pair
erase() 输入的参数是pair或者迭代器
find() 查找一个数
能像数组一样来用,时间复杂度是o(logn)
map<string,int> a;
a["asac"]=1;
cout<<a["szc"]<<endl;
unordered_set,unordered_map,unordered_multiset,unordered_multimap,哈希表
和上面类似,增删改查的时间复杂度是o(1)
不支持 lower_bound()/uper_bound() ,迭代器的++,--
bitset,压位(省八位空间)
1024个bool(一字节)
1024 B =1KB
如果压到每一位去,每个字节存8位,只需要开128个字节
bitset<10000> s;
~,&,|,^
>>,<<
==,!=
[] 取出某一位是0,是1
count() 返回有多少个1
any() 判断是否至少有一个1
none() 判断是否全为0
set() 把所有位置b变成1
set(k,v) 把第k位变成v
reset() 把所有位变成0
flip() 等价于~
flip(k) 把第k位取反