出题时看到这道陈题,以为会很水,后来发现还是蛮有研究价值的样子。
首先我使用平时读整行int数据的方式,也就是下面的代码,结果毫不留情地TLE了,不得不换一种更麻烦的姿势。看来这样写确实方便,但速度并不快。
char buf[MAXN];
gets(buf);
int v;
char *p=strtok(buf," ");
while(p) {
sscanf(p,"%d",&v);
p=strtok(NULL," ");
}
其次这道题确实很水,因为数据有bug,题目说数据量不超过10^6,结果一测试发现数据本身的值也不超过10^6,这样完全用不到hash的样子……这道题时限1500ms,结果600+ms水过。
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=1000005;
bool data[MAXN];
int main() {
int n,m;
scanf("%d",&n);
while(n--) {
memset(data,false,sizeof(data));
int tmp;
char c;
while(true) {
scanf("%d%c",&tmp,&c);
data[tmp]=true;
if(c=='\n')
break;
}
scanf("%d",&m);
while(m--) {
scanf("%d",&tmp);
puts(data[tmp]?"Yes.":"No.");
}
}
}
然后我们不考虑这个bug,就当数据在int范围内可不可以做呢……当然可以。之前不是研究过unordered_map嘛。直接当hash表来用,结果1470+ms,几乎是压线过掉了。加了IO优化之后也有1300+ms。
#include<cstdio>
#ifdef __GXX_EXPERIMENTAL_CXX0X__
#include<unordered_map>
#else
#include<tr1/unordered_map>
#endif
using namespace std;
using namespace std::tr1;
unordered_map<int,bool> data;
int main() {
int n,m;
scanf("%d",&n);
while(n--) {
data.clear();
int tmp;
char c;
while(true) {
scanf("%d%c",&tmp,&c);
data[tmp]=true;
if(c=='\n')
break;
}
scanf("%d",&m);
while(m--) {
scanf("%d",&tmp);
puts(data[tmp]?"Yes.":"No.");
}
}
}
然后切入正题,这道题用hash来做,其实就是把数字当作字符串来看待,尝试了多种hash函数和参数后,550ms左右的时间过掉。
我对hash并不熟,如果不是要出题,可能根本不会想到去研究hash。首先我强烈推荐这篇blog,它详尽地给出了多种hash的C++代码。而且我惊奇地发现,blog中得出字符串hash效果最好的BKDRHash函数,就是sd0061在用的hash函数,也是Thor被以前的函数虐惨后最近开始使用的函数,而且过掉了北京邀请赛某题……所以可以说这个测试结果基本令人信服。
我这里把目前搜集到的hash函数都放在这里(其实目前也就是那篇日志里的函数= =),至于过这道题需要的函数和对应参数,我先不写……一旦打算换个数据出题呢……放心换数据出题肯定不会让13级用前两种做法水过去的……
const int INF=10000019;
int hash[INF];
unsigned int BKDRHash(char *str) {
unsigned int seed=131;
unsigned int hash=0;
while(*str)
hash=hash*seed+(*str++);
return hash%INF;
}
unsigned int APHash(char *str) {
unsigned int hash=0;
for(int i=0; *str; ++i)
if((i&1)==0)
hash^=((hash<<7)^(*str++)^(hash>>3));
else
hash^=(~((hash<<11)^(*str++)^(hash>>5)));
return hash%INF;
}
unsigned int DJBHash(char *str) {
unsigned int hash=5381;
while(*str)
hash+=(hash<<5)+(*str++);
return hash%INF;
}
unsigned int JSHash(char *str) {
unsigned int hash=1315423911;
while(*str)
hash^=((hash<<5)+(*str++)+(hash>>2));
return hash%INF;
}
unsigned int RSHash(char *str) {
unsigned int b=378551;
unsigned int a=63689;
unsigned int hash=0;
while(*str) {
hash=hash*a+(*str++);
a*=b;
}
return hash%INF;
}
unsigned int SDBMHash(char *str) {
unsigned int hash=0;
while(*str)
hash=(*str++)+(hash<<6)+(hash<<16)-hash ;
return hash%INF;
}
unsigned int PJWHash(char *str) {
unsigned int BitsInUnignedInt=(unsigned int)(sizeof(unsigned int)*8);
unsigned int ThreeQuarters=(unsigned int)((BitsInUnignedInt*3)/4);
unsigned int OneEighth=(unsigned int)(BitsInUnignedInt/8);
unsigned int HighBits=(unsigned int)(0xFFFFFFFF)<<(BitsInUnignedInt-OneEighth);
unsigned int hash=0;
unsigned int test=0;
while(*str) {
hash=(hash<<OneEighth)+(*str++);
if((test=hash&HighBits)!=0)
hash=((hash^(test>>ThreeQuarters))&(~HighBits));
}
return hash%INF;
}
unsigned int ELFHash(char *str) {
unsigned int hash=0;
unsigned int x=0;
while(*str) {
hash=(hash<<4)+(*str++);
if((x=hash&0xF0000000L)!=0) {
hash^=(x>>24);
hash&=~x;
}
}
return hash%INF;
}
练习赛结束了,在此基础上出了一道题,需要拉链法处理冲突,标程在此。现在把上面那道题的代码放一下。
#include<cstdio>
#include<cstring>
using namespace std;
const int INF=10000019;
bool hash[INF];
unsigned int DJBHash(char *str) {
unsigned int hash=5381;
while(*str)
hash+=(hash<<5)+(*str++);
return hash%INF;
}
int main() {
int n,m;
scanf("%d",&n);
while(n--) {
char c,str[15];
memset(hash,false,sizeof(hash));
while(true) {
while(c=getchar(),(c<'0'||c>'9')&&(c!='-'));
int cnt=0;
while((c>='0'&&c<='9')||(c=='-')) {
str[cnt++]=c;
c=getchar();
}
str[cnt]='\0';
hash[DJBHash(str)]=true;
if(c=='\n')
break;
}
scanf("%d",&m);
while(m--) {
while(c=getchar(),(c<'0'||c>'9')&&(c!='-'));
int cnt=0;
while((c>='0'&&c<='9')||(c=='-')) {
str[cnt++]=c;
c=getchar();
}
str[cnt]='\0';
puts(hash[DJBHash(str)]?"Yes.":"No.");
}
}
}