乱七八糟的Hash函数

  出题时看到这道陈题,以为会很水,后来发现还是蛮有研究价值的样子。

  首先我使用平时读整行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.");
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值