哈希 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种特性总数的数组)转化为一个整数,可以采用

  1. unsigned int result = 0;
  2. for(int k = 0; k < M; k ++){
  3. result += A[i][k]*(k+1);
  4. }

也可以采用其他函数。

哈希算法实现(c++)
  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include<stdio.h>
  3. #include<string.h>
  4. #define MAX(a, b) a>b? a:b
  5. #define MAX_COW_NUM 100002 //牛的总数
  6. #define MAX_FEATURE_NUM 31 //特性总数
  7. #define HASH_TABLE_SIZE 99995 //哈希表大小
  8.  
  9. struct Node{ //节点信息,next表示在用拉链法来解决哈希冲突的时候的链表后续节点指针
  10. int index; //用于表示被哈希的 复杂数据
  11.  
  12. int next; //这里只使用一个int型的index来表示复杂数据(一个M个元素的整数数组)在全局的
  13. }; //int gCowSeqFeatureCount[MAX_COW_NUM][MAX_FEATURE_NUM];中的位置
  14.  
  15. Node gNodes[MAX_COW_NUM]; //存储所有节点信息的全局数组
  16. int gHashTable[HASH_TABLE_SIZE]; //哈希表,其中每个数后可能跟着一条Node链表
  17.  
  18. int gCowSeqFeatureCount[MAX_COW_NUM][MAX_FEATURE_NUM]; //需要被哈希表示的复杂数据,即前i头牛的所有M中特性的总数数组
  19. int gFeatures[MAX_COW_NUM]; //存放输入N头牛的特性信息
  20.  
  21. int gFeatureNum, gCowNum;
  22.  
  23. int gMaxSeqLength = 0; //好序列长度
  24. int gNodeCount = 0; //用于构造哈希表的全局变量,指向当前节点在 gNodes中的索引
  25.  
  26. //将表示牛的特性的一个整数转换为M个元素的数组
  27. void ParseFeature(int feature, int* feature_array){
  28. for (int k = 0; k < gFeatureNum; k++){
  29. feature_array[k] = feature & 0x01;
  30. feature = feature >> 1;
  31. }
  32. }
  33.  
  34. //初始化,将 Node 节点的next都置为-1,表示链表没有后续节点
  35. void Init(){
  36. memset(gCowSeqFeatureCount, 0, sizeof(gCowSeqFeatureCount));
  37. for (int i = 0; i < HASH_TABLE_SIZE; i++){
  38. gHashTable[i] = -1;
  39. }
  40. }
  41.  
  42. //比较两个序列是否相同
  43. bool Compare(int* array1, int* array2){
  44. for (int k = 0; k < gFeatureNum; k++){
  45. if (array1[k] != array2[k])
  46. return false;
  47. }
  48. return true;
  49. }
  50.  
  51. //判断数组中的数是否都相同
  52. bool IsAllSame(int n){
  53. for (int k = 1; k < gFeatureNum; k++){
  54. if (gCowSeqFeatureCount[n][k] != gCowSeqFeatureCount[n][k - 1]){
  55. return false;
  56. }
  57. }
  58. return true;
  59. }
  60.  
  61. //哈希函数
  62. unsigned int GetHash(int* feature_array){
  63. unsigned int result = 0;
  64. for (int k = 0; k < gFeatureNum; k++){
  65. result += feature_array[k]*(k+1);
  66. }
  67. return (result&0x7FFFFFFF) % HASH_TABLE_SIZE;
  68. }
  69.  
  70. //插入一个新的元素到哈希表中
  71. void Insert(int k, unsigned int hash){
  72. gNodes[gNodeCount].next = gHashTable[hash];
  73. gNodes[gNodeCount].index = k;
  74. gHashTable[hash] = gNodeCount;
  75. gNodeCount++;
  76. }
  77.  
  78. //查找
  79. void Search(int k){
  80. unsigned int h = GetHash(gCowSeqFeatureCount[k]);
  81. int node_index = gHashTable[h]; //按照链查找
  82. while (node_index != -1){
  83. if (Compare(gCowSeqFeatureCount[k], gCowSeqFeatureCount[gNodes[node_index].index])){
  84. gMaxSeqLength = MAX(gMaxSeqLength, k - gNodes[node_index].index);
  85. return; //这里是一个剪枝,当之前有序列p 和序列k 相同,则之后 序列k+1, k+2, ...
  86. //如果和序列k相同,则必定与p相同,且p到k+1, k+2的长度显然要大于k到k+1,k+2的长度
  87. }
  88. node_index = gNodes[node_index].next;
  89. }
  90. //插入到表头
  91. Insert(k, h);
  92. }
  93.  
  94. int main(){
  95. Init();
  96. scanf("%d %d", &gCowNum, &gFeatureNum);
  97. int feature_array[MAX_FEATURE_NUM];
  98. for (int i = 0; i < gCowNum; i++){
  99. scanf("%d", &gFeatures[i]);
  100. ParseFeature(gFeatures[i], feature_array); //将表示牛的特性的整数转换为一个M个特性元素的数组
  101. if (i > 0){
  102. for (int k = 0; k < gFeatureNum; k++){ //获得前i头牛的M种特性的总数数组
  103. gCowSeqFeatureCount[i][k] += gCowSeqFeatureCount[i-1][k];
  104. }
  105. }
  106. for (int k = 0; k < gFeatureNum; k++){
  107. gCowSeqFeatureCount[i][k] += feature_array[k];
  108. }
  109. }
  110.  
  111. for (int n = gCowNum - 1; n >= 0; n--){
  112. if (IsAllSame(n)){
  113. gMaxSeqLength = n + 1;
  114. break;
  115. }
  116. }
  117.  
  118. //归一化
  119. for (int i = 0; i < gCowNum; i++){
  120. for (int k = 0; k < gFeatureNum; k++){
  121. gCowSeqFeatureCount[i][k] -= gCowSeqFeatureCount[i][gFeatureNum - 1];
  122. }
  123. }
  124.  
  125. //查找同时插入
  126. for (int i = 0; i < gCowNum; i++){
  127. Search(i);
  128. }
  129. printf("%d\n", gMaxSeqLength);
  130. return 0;
  131. }

实现2:

  1. #include<stdio.h>
  2. #include<iostream>
  3. #include<string>
  4. #include<string.h>
  5. #include<vector>
  6. #include<stack>
  7. #include<queue>
  8. #include<deque>
  9. #include<list>
  10. #include<set>
  11. #include<set>
  12. #include<map>
  13. #include<functional>
  14. #include<algorithm>
  15. using namespace std;
  16. #define mod 100007
  17. struct Node{
  18. int index;
  19. int next;
  20. };
  21. Node gNodes[100007];
  22. int gHead[mod];
  23. int gFeatureSum[100005][32];
  24. int maxSpan;
  25. int gNodeIndex;
  26. int N, K;
  27. void Init(){
  28. memset(gNodes, -1, sizeof(gNodes));
  29. memset(gHead, -1, sizeof(gHead));
  30. maxSpan = 0;
  31. gNodeIndex = 0;
  32. }
  33. void Insert(int hash, int index){
  34. int h = gHead[hash];
  35. while (h != -1){
  36. int tmp = gFeatureSum[index][0] - gFeatureSum[gNodes[h].index][0];
  37. bool flag = true;
  38. for (int i = 1; i < K; i++){
  39. if (tmp != gFeatureSum[index][i] - gFeatureSum[gNodes[h].index][i]){
  40. flag = false;
  41. break;
  42. }
  43. }
  44. if (flag){
  45. maxSpan = max(maxSpan, index - gNodes[h].index);
  46. return;
  47. }
  48. h = gNodes[h].next;
  49. }
  50. gNodes[gNodeIndex].index = index;
  51. gNodes[gNodeIndex].next = gHead[hash];
  52. gHead[hash] = gNodeIndex++;
  53. }
  54. int main(){
  55. scanf("%d %d", &N, &K);
  56. Init();
  57. int feature;
  58. for (int i = 1; i <= N; i++){
  59. scanf("%d", &feature);
  60. int bit = 0;
  61. int tmp[32] = { 0 };
  62. bool all_same = true;
  63. int hash = 0;
  64. for(int bit = 0; bit < K; bit ++){
  65. gFeatureSum[i][bit] = gFeatureSum[i - 1][bit] + ((feature >> bit)& 1);
  66. if (bit > 0){
  67. tmp[bit] = gFeatureSum[i][bit] - gFeatureSum[i][0];
  68. hash += tmp[bit];
  69. if (tmp[bit] != 0)
  70. all_same = false;
  71. }
  72. }
  73. if (all_same){
  74. maxSpan = i;
  75. continue;
  76. }
  77. Insert((hash % mod + mod) % mod, i);
  78. }
  79. printf("%d\n", maxSpan);
  80. return 0;
  81. }

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. LAN8710A/LAN8710Ai datasheet 记录

    因为产品的双网口出现了问题,而且是AM335x 内部驱动,难度比较大,现从PHY 端开始分析相关原理,找到双网口不能使用的原因. 此篇是记录一些有关LAN8710A 这个PHY的一些特性. 各个功能模 ...

  2. kettle的报错解决机制

    在kettle执行的过程中,如果遇到错误,kettle会停止运行.在某些时候,并不希望kettle停止运行,这时候可以使用错误处理(Step Error Handling).错误处理允许你配置一个步骤 ...

  3. js学习笔记34----自执行函数

    自执行函数的写法通常如下: 方式1: (function(){ *** 写事件处理代码 *** }()) 方式2: (function(){ *** 写事件处理代码 *** })() 方式3: !(f ...

  4. j2se j2ee j2me

    多数编程语言都有预选编译好的类库以支持各种特定的功能,在Java中,类库以包(package)的形式提供,不同版本的Java提供不同的包,以面向特定的应用. Java2平台包括标准版(J2SE).企业 ...

  5. ubuntu16.04 安装caffe以及python接口

    http://blog.csdn.net/qq_25073253/article/details/72571714http://blog.csdn.net/greed7480/article/deta ...

  6. peek函数的用法

    #include <iostream> /* run this program using the console pauser or add your own getch, system ...

  7. JAVA中String字符串比较equals()和equalsIgnoreCase()的区别

    标签: equals和equalsIgnoreC 2012-11-11 16:03 65644人阅读 评论(0) 收藏 举报  分类: JAVA(3)  1.使用equals( )方法比较两个字符串是 ...

  8. (转)YV12 and NV12

    用videoCapture和IAMStreamConfig拿到的支持的格式列表.发现支持2中图像格式,YV12和NV12.具体是怎么样的内存分布不知道.查了些文档.自己修改了几个图.看出了点端倪 YV ...

  9. (转)非阻塞Connect对于select时应注意问题

    对于面向连接的socket类型(SOCK_STREAM,SOCK_SEQPACKET)在读写数据之前必须建立连接,首先服务器端socket必须在一个客户端知道的地址进行监听,也就是创建socket之后 ...

  10. linux -- ubuntu 何为软件源

    新手学Ubuntu的时候,一般不知道什么是源,但源又是Ubuntu下常用到的东西.因此,本文就详细介绍一下Ubuntu 源. 什么是软件源? 源,在Ubuntu下,它相当于软件库,需要什么软件,只要记 ...