哈希 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\n", 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\n", maxSpan);
return 0;
}

poj_3274 哈希的更多相关文章

  1. [PHP内核探索]PHP中的哈希表

    在PHP内核中,其中一个很重要的数据结构就是HashTable.我们常用的数组,在内核中就是用HashTable来实现.那么,PHP的HashTable是怎么实现的呢?最近在看HashTable的数据 ...

  2. java单向加密算法小结(2)--MD5哈希算法

    上一篇文章整理了Base64算法的相关知识,严格来说,Base64只能算是一种编码方式而非加密算法,这一篇要说的MD5,其实也不算是加密算法,而是一种哈希算法,即将目标文本转化为固定长度,不可逆的字符 ...

  3. Java 哈希表运用-LeetCode 1 Two Sum

    Given an array of integers, find two numbers such that they add up to a specific target number. The ...

  4. 网络安全——Base64编码、MD5、SHA1-SHA512、HMAC(SHA1-SHA512)哈希

    据说今天520是个好日子,为什么我想起的是502.500.404这些?还好服务器没事! 一.Base64编码 Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之 ...

  5. Oracle 哈希连接原理

    <基于Oracle的sql优化>里关于哈希连接的原理介绍如下: 哈希连接(HASH JOIN)是一种两个表在做表连接时主要依靠哈希运算来得到连接结果集的表连接方法. 在Oracle 7.3 ...

  6. SQL连接操作符介绍(循环嵌套, 哈希匹配和合并连接)

    今天我将介绍在SQLServer 中的三种连接操作符类型,分别是:循环嵌套.哈希匹配和合并连接.主要对这三种连接的不同.复杂度用范例的形式一一介绍. 本文中使用了示例数据库AdventureWorks ...

  7. BZOJ 3555: [Ctsc2014]企鹅QQ [字符串哈希]【学习笔记】

    3555: [Ctsc2014]企鹅QQ Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 2046  Solved: 749[Submit][Statu ...

  8. [bzoj3207][花神的嘲讽计划Ⅰ] (字符串哈希+主席树)

    Description 背景 花神是神,一大癖好就是嘲讽大J,举例如下: “哎你傻不傻的![hqz:大笨J]” “这道题又被J屎过了!!” “J这程序怎么跑这么快!J要逆袭了!” …… 描述 这一天D ...

  9. minHash最小哈希原理

    minHash最小哈希原理 收藏 初雪之音 发表于 9个月前 阅读 208 收藏 9 点赞 1 评论 0 摘要: 在数据挖掘中,一个最基本的问题就是比较两个集合的相似度.通常通过遍历这两个集合中的所有 ...

随机推荐

  1. C语言 · 五次方数

    算法提高 五次方数   时间限制:1.0s   内存限制:256.0MB      问题描述 对一个数十进制表示时的每一位数字乘五次方再求和,会得到一个数的五次方数 例如:1024的五次方数为1+0+ ...

  2. 历届蓝桥杯C/C++省赛试题

    2012年第三届蓝桥杯C/C++程序设计本科B组省赛 2013年第四届蓝桥杯C/C++程序设计本科B组省赛 2014年第五届蓝桥杯C/C++程序设计本科B组省赛 2015年第六届蓝桥杯C/C++程序设 ...

  3. CSS编写指导规范和建议

    在参与规模庞大.历时漫长且参与人数众多的项目时,所有开发者遵守如下规则极为重要: 保持 CSS 易于维护 保持代码清晰易懂 保持 CSS 的可拓展性 为了实现这一目标,我们要采用诸多方法. 本文档第一 ...

  4. 移动H5功能设计反思 测试用例总结

    一.线上页面滑动流畅性测试 1.减少长动画效果(影响流畅) 2.是否自动跳转或者还是让用户自己操作跳转需要推敲 二.buttom和页面滑动的选择(优劣) 部分手机本身就会滑动不灵敏,大部分时候其实用b ...

  5. Qt中的串口编程之一

    QtSerialPort 简介 功能介绍 SerialPort SerialPortInfo 源代码 编译和安装 配置编译环境 Perl只是在Qt5的时候才需要Qt4的情况下可以不配置 使用如下推荐步 ...

  6. 关于Cocos2d-x中定时器的使用总结

    1.定义 定时器在大部分游戏中是不可或缺的,即每隔一段时间,就要执行相应的刷新体函数,以更新游戏的画面.时间.进度.敌人的指令等等. cocos2dx为我们提供了定时器schedule相关的操作.其操 ...

  7. 关于Cocos2d-x中让主角运动的方法

    比如要让角色跳起来 1.如果是用到物理引擎,那么在物理世界中,可以用 hero->getPhysicsBody()->setVelocity(Vec2(0, 400));  //给主角一个 ...

  8. Android.mk文件c++头文件包含问题

    Eclipse 中 Android.mk文件c++头文件包含问题 jni中的目录结构如下: 编译找不到头文件 LOCAL_PATH := $(call my-dir)LOCAL_C_INCLUDES ...

  9. e664. 在图像中获取子图像

    // From an Image image = createImage(new FilteredImageSource(image.getSource(), new CropImageFilter( ...

  10. c++ vector init操作

    1) string str[n]={"hello", ...}; vector<string> strArray(str,str+n); 2) vector<st ...