哈希 Hash
哈希是一种将复杂数据转换为线性数据从而可以进行随机访问的查找算法。
哈希经常用于对复杂数据(如结构体、对象等)的查找,在使用的时候,需要定义一个Hash函数,将需要查找的复杂对象转化为 整型(或其他简单的数据类型)数据,将得到的数据作为该复杂对象的键值key(如果有多个不同的复杂数据对象对应相同的键值key,则使用开放定址法或者拉链法来解决冲突。
哈希算法将复杂的数据对象转换为简单的键值类型,然后进行查找。这样再查找的时候可以实现随机访问,从而大大提高了查找的速度。
poj 3274 Gold Balanced Lineup
题目大意
有N头牛,每个牛身上有一些特性,总的特性类型有M种(M < 31)。每个牛用一个32位整数表示,从该整数的最低位到最高位,依次表示牛身上是否具有第k中特性(如果第k位为1,表示该牛身上具有第k中特性,否则没有)。
定义一个“好序列”: Ai, Ai+1, Ai+2, .... Aj 为连续的一个数组,分别表示第i头到第j头牛的特性,如果这组牛中身上具有的M种特性,每种特性的数目都相同,则该序列为一个“好序列”。
输入N个数,代表第1到第N头牛的特性。求出 最长的“好序列”的长度。
分析
得到所有牛的信息之后,可以从前向后查找,对于当前的牛 Cow(i), 从Cow(0)依次查找到Cow(i-1),查看是否有牛j满足,Cow(j)到Cow(i)中间序列是否满足“好序列”的性质。可以使用数组A保存每头牛及其之前所有牛的M种特性中每种特性的总个数,在判断是否为“好序列”的时候,用 A[i][k] - A[j][k] 表示第j到第i头牛第k种特性的总数,比较M种特性的总数是否相同,则可以判断是否为好序列。
若A[i] = (a, b, c), A[j] = (d, e, f),则如果 a-d = b-e = c-f,则表示Cow(i)到Cow(j)为一个“好序列”;进一步可以归一化 是为了实现哈希函数为(a-c, b-c, 0) = (d-f, e-f, 0)。
这就转换为一个查找问题,但是直接存储并比较M个数,比较费时,可以将数组 A[i] (A为二维数组,A[i]表示取出第二维,表示 前i头牛身上的特性序列,共M个数)
(1)进行归一化(即A[i]中的每个数,都减去最后一个数(A[i][k] -= A[i][M-1]);
(2)然后通过Hash函数转换为一个非负整数的键值key,使用哈希表进行存储。
这样,如果A[i]和A[j]的hash键值相同,则说明Cow(i)到Cow(j)可能为“好序列”,这时候再去判断从Cow(i)到Cow(j)每种特性总数是否相同。
哈希函数Hash是要将归一化后的A[i](前i头牛的M种特性总数的数组)转化为一个整数,可以采用
unsigned int result = 0; for(int k = 0; k < M; k ++){ result += A[i][k]*(k+1); }
也可以采用其他函数。
哈希算法实现(c++)
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h> #define MAX(a, b) a>b? a:b #define MAX_COW_NUM 100002 //牛的总数 #define MAX_FEATURE_NUM 31 //特性总数 #define HASH_TABLE_SIZE 99995 //哈希表大小 struct Node{ //节点信息,next表示在用拉链法来解决哈希冲突的时候的链表后续节点指针 int index; //用于表示被哈希的 复杂数据 int next; //这里只使用一个int型的index来表示复杂数据(一个M个元素的整数数组)在全局的 }; //int gCowSeqFeatureCount[MAX_COW_NUM][MAX_FEATURE_NUM];中的位置 Node gNodes[MAX_COW_NUM]; //存储所有节点信息的全局数组 int gHashTable[HASH_TABLE_SIZE]; //哈希表,其中每个数后可能跟着一条Node链表 int gCowSeqFeatureCount[MAX_COW_NUM][MAX_FEATURE_NUM]; //需要被哈希表示的复杂数据,即前i头牛的所有M中特性的总数数组 int gFeatures[MAX_COW_NUM]; //存放输入N头牛的特性信息 int gFeatureNum, gCowNum; int gMaxSeqLength = 0; //好序列长度 int gNodeCount = 0; //用于构造哈希表的全局变量,指向当前节点在 gNodes中的索引 //将表示牛的特性的一个整数转换为M个元素的数组 void ParseFeature(int feature, int* feature_array){ for (int k = 0; k < gFeatureNum; k++){ feature_array[k] = feature & 0x01; feature = feature >> 1; } } //初始化,将 Node 节点的next都置为-1,表示链表没有后续节点 void Init(){ memset(gCowSeqFeatureCount, 0, sizeof(gCowSeqFeatureCount)); for (int i = 0; i < HASH_TABLE_SIZE; i++){ gHashTable[i] = -1; } } //比较两个序列是否相同 bool Compare(int* array1, int* array2){ for (int k = 0; k < gFeatureNum; k++){ if (array1[k] != array2[k]) return false; } return true; } //判断数组中的数是否都相同 bool IsAllSame(int n){ for (int k = 1; k < gFeatureNum; k++){ if (gCowSeqFeatureCount[n][k] != gCowSeqFeatureCount[n][k - 1]){ return false; } } return true; } //哈希函数 unsigned int GetHash(int* feature_array){ unsigned int result = 0; for (int k = 0; k < gFeatureNum; k++){ result += feature_array[k]*(k+1); } return (result&0x7FFFFFFF) % HASH_TABLE_SIZE; } //插入一个新的元素到哈希表中 void Insert(int k, unsigned int hash){ gNodes[gNodeCount].next = gHashTable[hash]; gNodes[gNodeCount].index = k; gHashTable[hash] = gNodeCount; gNodeCount++; } //查找 void Search(int k){ unsigned int h = GetHash(gCowSeqFeatureCount[k]); int node_index = gHashTable[h]; //按照链查找 while (node_index != -1){ if (Compare(gCowSeqFeatureCount[k], gCowSeqFeatureCount[gNodes[node_index].index])){ gMaxSeqLength = MAX(gMaxSeqLength, k - gNodes[node_index].index); return; //这里是一个剪枝,当之前有序列p 和序列k 相同,则之后 序列k+1, k+2, ... //如果和序列k相同,则必定与p相同,且p到k+1, k+2的长度显然要大于k到k+1,k+2的长度 } node_index = gNodes[node_index].next; } //插入到表头 Insert(k, h); } int main(){ Init(); scanf("%d %d", &gCowNum, &gFeatureNum); int feature_array[MAX_FEATURE_NUM]; for (int i = 0; i < gCowNum; i++){ scanf("%d", &gFeatures[i]); ParseFeature(gFeatures[i], feature_array); //将表示牛的特性的整数转换为一个M个特性元素的数组 if (i > 0){ for (int k = 0; k < gFeatureNum; k++){ //获得前i头牛的M种特性的总数数组 gCowSeqFeatureCount[i][k] += gCowSeqFeatureCount[i-1][k]; } } for (int k = 0; k < gFeatureNum; k++){ gCowSeqFeatureCount[i][k] += feature_array[k]; } } for (int n = gCowNum - 1; n >= 0; n--){ if (IsAllSame(n)){ gMaxSeqLength = n + 1; break; } } //归一化 for (int i = 0; i < gCowNum; i++){ for (int k = 0; k < gFeatureNum; k++){ gCowSeqFeatureCount[i][k] -= gCowSeqFeatureCount[i][gFeatureNum - 1]; } } //查找同时插入 for (int i = 0; i < gCowNum; i++){ Search(i); } printf("%d ", gMaxSeqLength); return 0; }
实现2:
#include<stdio.h> #include<iostream> #include<string> #include<string.h> #include<vector> #include<stack> #include<queue> #include<deque> #include<list> #include<set> #include<set> #include<map> #include<functional> #include<algorithm> using namespace std; #define mod 100007 struct Node{ int index; int next; }; Node gNodes[100007]; int gHead[mod]; int gFeatureSum[100005][32]; int maxSpan; int gNodeIndex; int N, K; void Init(){ memset(gNodes, -1, sizeof(gNodes)); memset(gHead, -1, sizeof(gHead)); maxSpan = 0; gNodeIndex = 0; } void Insert(int hash, int index){ int h = gHead[hash]; while (h != -1){ int tmp = gFeatureSum[index][0] - gFeatureSum[gNodes[h].index][0]; bool flag = true; for (int i = 1; i < K; i++){ if (tmp != gFeatureSum[index][i] - gFeatureSum[gNodes[h].index][i]){ flag = false; break; } } if (flag){ maxSpan = max(maxSpan, index - gNodes[h].index); return; } h = gNodes[h].next; } gNodes[gNodeIndex].index = index; gNodes[gNodeIndex].next = gHead[hash]; gHead[hash] = gNodeIndex++; } int main(){ scanf("%d %d", &N, &K); Init(); int feature; for (int i = 1; i <= N; i++){ scanf("%d", &feature); int bit = 0; int tmp[32] = { 0 }; bool all_same = true; int hash = 0; for(int bit = 0; bit < K; bit ++){ gFeatureSum[i][bit] = gFeatureSum[i - 1][bit] + ((feature >> bit)& 1); if (bit > 0){ tmp[bit] = gFeatureSum[i][bit] - gFeatureSum[i][0]; hash += tmp[bit]; if (tmp[bit] != 0) all_same = false; } } if (all_same){ maxSpan = i; continue; } Insert((hash % mod + mod) % mod, i); } printf("%d ", maxSpan); return 0; }