PKU ACM 3274 数组hash

来源:互联网 发布:什么打字软件好 编辑:程序博客网 时间:2024/05/29 18:56
先说说我对数组哈希的理解,加入现在我们有 按照顺序输入若干个数组,然后找到其中一对相等的数组。
很显然这个问题,如果用暴力的方法复杂度为O(n^2), 如果借用排序的话 复杂度可以变成 n * log(n)

因为我们的目的仅仅是找到一对相同的数组,所以其实我们没有必要排序,因为排序处理了所有元素,自然看起来没有那么必要,这个时候,我们可以借用hash的思想,将一个数组映射到一个数字,然后,如果两个数组映射到相同的数字,那么他们可能是相等的,也可能是冲突,通过比较原来的数组,就能知道两个数组是不是一样了。如果hash函数设计的比较好的话,效率可以达到 O(n),但是要付出额外的存储代价!

假设 unsigned int hashcode(char *v, int size)返回数组的哈希值
int hash[1000000] 是一个哈希的bucket,采用开放地址法
则工作流程如下
INPUT nArray //输入数组的数量
WHILE( i < nArray)
{
    INPUT Array[i];
    code = hashcode(Array[i]);
    while(hash[code] != -1) //用开放地址法
    {
         if(Array[hash[code]] EQUAL Array[i])
                   // Array[i] == Array[hash[code]]
         else
              code++;//开放地址递增
    }
    if(hash[code] == -1) // 之前没有人存储过--处女地
         hash[code] = i;
    i++;
}


这里一个比较关键的一点是如何设计比较好的哈希函数,哈希函数设计的好坏直接决定效率,例如如果哈希函数聚集严重(生成的哈希code都是1)这个时候的复杂度就变成了 n^2 完全没有任何优势
UNIX里面有一个字符串哈希的构造法,不过不知道为什么这么构造,可能蕴含某种高深的数学原理
int ELFhash(char *key)

    unsigned long h=0;
    while(*key)
    { 
        h=(h<<4)+*key++;
        unsigned long g=h&0Xf0000000L;
        if(g) h^=g>>24;
        h&=~g;
    }
    return h%MOD;
}


下面来说这道OJ上的题目
假设 a[i][j] 存储的是 第 i 头牛的 第j个属性,sum[i][j] 存储的是 a[0][j] + ... a[i][j] 的值
我们要找到就是 sum[i][0] - sum[j][0] == sum[i][1] - sum[j][1] == sum[i][2] - sum[j][2] == sum[i][k-1] - sum[j][k-1] 成立的最大 i - j
转换为 
sum[i][1] - sum[i][0] = sum[j][1] - sum[j][0];
sum[i][2] - sum[i][0] = sum[j][2] - sum[j][0];
..
sum[i][k-1] - sum[i][0] = sum[j][k-1] - sum[j][0];
同时成立 等价于
转化为这么写的好处,使得一侧只有 i 另一侧只有j,这样在一次扫描采用哈希就可以完成任务了
如何利用哈希呢
C[i][1..k-1] = sum[i][1..k-1] - sum[i][0]
...
然后 哈希 C[i], 这样就可以一边读入一边找了,当找到哈希一样的两个C,然后找出之前的j,当前为i,求出最大 i-j


#include <stdio.h>#include <string.h>#define maxn 100010const int prime = 99983;int n,k;int a[maxn][31],sum[maxn][31],C[maxn][31];int hash[1000000];int ans;unsigned int hashcode(int *v,int k){int i;unsigned int p = 0;for( i = 0; i < k; i++){p += v[i] << i; //p = ((p<<2)+(v[i]>>4))^(v[i]<<10);}p = p % prime;return p;}int main(){int i,j;int code;int v[31];memset(hash,-1,sizeof(hash));memset(sum,0,sizeof(sum));memset(C,0,sizeof(C));scanf("%d%d",&n,&k);hash[hashcode(C[0],k)] = 0;unsigned int p;ans = 0;for( i = 1; i <= n; i++){//假设第一个元素的hashcode是0scanf("%d",&code);for( j = 0; j < k; j++){a[i][j] = code % 2;code >>= 1;sum[i][j] += sum[i-1][j] + a[i][j];C[i][j] = sum[i][j] - sum[i][0];}p = hashcode(C[i],k);while(hash[p] != -1){for( j = 1; j < k; j++){if(C[hash[p]][j] != C[i][j] ){break;}}if(j == k){if(i - hash[p] > ans){ans = i - hash[p];}break;}p++;}if( hash[p] == -1){hash[p] = i;}}printf("%d\n",ans);}

原创粉丝点击