寒假周练二
散列存储
字符串关键字的散列函数构造
把字符串对象映射成整数
一、简单粗暴方法:
把字符串中每个字符的ASCLL码相加对mod求余
一来会有冲突,二来每个字符ASCLL码范围 0 ~127
表示范围有限,要映射的字符串很广,很容易产生聚集
二、简单的改进,前3个字符移位法 。
把挑出的这三个字符(比如每个单词前三个字符)看成27进制数的三个进制位上的数,(26个字母和可能有的空格
还是很有可能冲突,string,struct,strlen
造成地址空间的浪费 ,经统计前三个字符出现的可能情况有3000中,而开的空间有
2
6
3
26^3
263
三、涉及关键字n个字符,统统做关键字映射
不用27进制,用32进制数,因为
该
位
数
值
∗
32
该位数值*32
该位数值∗32,可以表示成
该
为
数
值
<
<
5
该为数值<<5
该为数值<<5
多项式的计算 和 一个n进制数的求值 类似噢
分别累加各位数值进行相加很不聪明,而应该
(
x
1
x_1
x1*n+
x
2
x_2
x2)*n+
x
3
x_3
x3*n+
x
4
x_4
x4 ……
比如十进制数
while(len–){
h=str[i]+h*10;
}
数字关键词的散列函数构造
p取素数可以使得映射分布均匀
散列存储解决冲突的方法
开放地址法:
1、线性探测法、2、平方探测法
B. Peculiar Movie Preferences
https://codeforces.com/contest/1628/problem/B
#include <iostream>
#include<string.h>
#include <string>
#include <algorithm>
//#include<bits/stdc++.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
const int N=1e5+5;
#include<vector>
int vis[N];
int cnt[N];
//数组对一个序列中出现的int数据类型出现的次数好累计,对string字符串
//出现的次数不太好累计 由于此题字符串长度不超过3,可以将字符串映射成
//27进制数0~26 否则可以试着用map容器,让字符串对应它总共和出现次数
int Hash(string str){
int len=str.size();
int v=0;
for(int i=0;i<len;i++){
v=v*27+str[i]-'a'+1;//a是1,空字串是0
}
return v;
}
int main(int argc, char** argv) {
int t;
cin>>t;
int n;
while(t--){
cin>>n;
memset(vis,0,sizeof(vis));
memset(cnt,0,sizeof(cnt));
vector<string> v;
string str;
for(int i=0;i<n;i++){
cin>>str;
v.push_back(str);
vis[Hash(str)]++;
}
//不管字符串长度1\2\3,只要自己是回文的 或 能在s之后出现rev(s)一定回文
//字符串长度为2时,12321,可以允许额外情况,头部搭一个+rev(s)
//字符串长度为3时 123只要前两个字符的rev在后面能遇见
int flag=0;
for(int i=0;i<n;i++){
string s=v[i];
char ch1=s[0];
cnt[Hash(s)]++;
reverse(s.begin(),s.end());
char ch2=s[0];
// if(s[0]==s.back())flag=1;//翻转后也是回文啦,满足就不做了
if(ch1==ch2)flag=1;
else{
if(cnt[Hash(s)]<vis[Hash(s)]){//后面还会遇到
flag=1;
}
}
if(s.size()==2){
for(int i=0;i<26;i++){
string m=char('a'+i)+s;
if(cnt[Hash(m)]<vis[Hash(m)])flag=1;
}
}
else if(s.size()==3){//123 翻转后就是后两个字符
string m=s.substr(1);
if(cnt[Hash(m)]<vis[Hash(m)]){//后面还会遇到
flag=1;
}
}
}
if(flag)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
A. Meximum Array用set容器存储之前遍历过的序列以便全局对照
#include <iostream>
#include<string.h>
//#include<bits/stdc++.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
const int N=2e5+5;
int a[N];
int vis[N];
int cnt[N];
#include <vector>
#include<set>
int main(int argc, char** argv) {
int t;
cin>>t;
int n;
while(t--){
cin>>n;
set<int> s;
//set容器自动去重,升序排列
//相比于vector容器,去重(因为已经用vis数组记录出现次数了
vector<int> res;
memset(vis,0,sizeof(vis));
memset(cnt,0,sizeof(cnt));
int mex=0;
for(int i=0;i<n;i++){
cin>>a[i];
vis[a[i]]++;
}
for(int i=0;i<n;i++){
cnt[a[i]]++;
//不能单单遍历,初始化mex为0,相等就增1不相等不变,因为序列无序,
//遇到相等元素之后增1可能就等于前面遇见过的元素
//因此之前遇见的元素需要全部储存到容器中,对于mex的选取,不能光
//与当前元素比较,相等就增1
//要保证与之前遇到的都不相等
//且对于容器中元素的存取 速率较高,便于 在一段序列中取得整个序列
//最大mex时,在后续序列中再重新取mex以便于增长mex序列
s.insert(a[i]);
while(s.count(mex))mex++;
if(cnt[mex]==vis[mex]){//或者vis[mex]==0
//vis[mex]==0不可以 因为已经找过一段序列的mex之后,找后
//面序列的mex不用考虑之前的序列(前面序列已移除
//之后不会再出现mex这个元素则此时的mex全局最大
s.clear();
res.push_back(mex);
mex=0;
}
}
cout<<res.size()<<endl;
for(int i=0;i<res.size();i++){
cout<<res[i]<<" ";
}
cout<<endl;
}
return 0;
}
GCD Arrays(思维:每个大于一的正整数都可以分解成若干个素因数幂次的乘积)
https://codeforces.com/contest/1629/problem/B
每个大于一的正整数都可以分解成若干个素因数幂次的乘积
只要保证数组钟每个数分解的质因数中包含相同的一个质因数
那么最容易达到的就是包含质因数2,只要将奇数换成其与偶数的乘积
#include <iostream>
//#include<bits/stdc++.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
typedef long long ll;
int main(int argc, char** argv) {
int t;
cin>>t;
ll l,r,k;
while(t--){
cin>>l>>r>>k;
// 每个大于一的正整数都可以分解成若干个素因数幂次的乘积
// 只要保证数组钟每个数分解的质因数中包含相同的一个质因数
// 那么最容易达到的就是包含质因数2,只要将奇数换成其与偶数的乘积
ll len=r-l+1;//3 4 5 6,4 5 6 7
if(len==1){
if(l==1)cout<<"NO"<<endl;
else cout<<"YES"<<endl;
continue;
}
ll cnt;
if((len&1)&&(l&1))cnt=(len>>1)+1;//位运算打括号
else cnt=len>>1;
if(k>=cnt)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
Not Sitting
https://codeforces.com/contest/1627/problem/B
Rahul在选定座位后,Tina一定会选择最远的点,显然四个角落都是Tina的备选方案。
有以上思路后不难发现,对于某一个位置,离它最远的点的距离是确定的,也是Tina会选择的点,其实这个点的答
案和Tina使用几桶油漆并没有关系。
理性分析一下,发现在最开始油漆少的时候Rahul可以选择中心的位置,此时的距离是最短的。
对于任意一个非中心的点,一定会存在一个角落使这两个点之间的距离比中心点到角落的距离更大。
所以当油漆少的时候他们的距离最近,随着油漆数量增加,他们之间的距离会逐渐增大。
由以上分析可知将每个点相对四个角的最远距离求出来,按照从小到大的顺序排列输出即可得到答案。
#include <iostream>
#include <algorithm>
//#include <string.h>
//#include<bits/stdc++.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
//typedef long long ll;//全换ll,一了百了
const int N=1e5+5;
int a[N];
int main(int argc, char** argv) {
int t;
cin>>t;
int m,n;
while(t--){
cin>>m>>n;
int c=m*n-1;
// for(int k=0;k<=c;k++){//k桶油漆
// }
int cnt=0;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
a[cnt++]=max(i-1,m-i)+max(j-1,n-j);
}
}
sort(a,a+cnt);
for(int i=0;i<=c;i++){
cout<<a[i]<<" ";
//0桶油漆就是 boy找一个位子之后girl找离他最远的角,找这种最大距离
// 的最小值在boy选哪个座时取得
// 多加一桶油漆就可以限制boy选座,把boy隔远一个距离(即girl选在油漆令侧)
}
cout<<endl;
}
return 0;
}