算法初步——散列

散列的定义与整数散列

  • 题目要求:M个预查询的数中每个数在N个数中出现的次数
    • 思路:设定一个int型数组 hashTable[10010] ,然后在输入 N 个数时进行预处理,即当输入的数为 x 时 ,就令 hashTable[x]++,时间复杂度为 O(N+M)
      #include <cstdio>
      #include <string>
      #include <algorithm>
      #include <cmath>
      using namespace std;
      
      const int maxn = 100010;
      int hashTable[maxn] = {0};
      
      int main() {
          int n, m, x;
          scanf("%d%d", &n, &m);
          
          for(int i=0; i<n; ++i) {
              scanf("%d", x);
              hashTable[x]++;        // 记录每个数出现次数 
          }
          for(int i=0; i<m; ++i) {    // 输出 M 个数中每个数在 N 个数中出现的次数 
              scanf("%d", x);
              printf("%d\n", hashTable[x]);
          } 
      
          return 0;
      }
  • 但是这个策略暂时还有一个问题——如果输入可能是10^9大小的整数,或者甚至是一个字符串,就不能将它们直接作为数组下标了,这时候可以使用散列,散列即 ” 将元素通过一个函数转换为整数,使得该整数可以尽量唯一的代表这个元素 “ ,常用的散列函数有 直接定址法、平方取中法、除留余数法等。
  • 除留余数法,H(key) = key % mod
  • 解决冲突的方法:线性探测法、平方探测法、链地址法

 

字符串hash初步

  • 先假设字符串均由大写字母 A~Z 构成
    • 不妨把 A~Z 视为 0~25 ,这样就把26个大写字母对应到二十六进制中
    • 接着按照将二十六进制转换为十进制的思路,便可实现将字符串映射为整数的需求
      int hashFunc(char S[], int len) {    // hash 函数,将字符串 S 转换为整数 
          int id = 0;
          for(int i=0; i<len; ++i) {
              id = id*26 + (S[i]-'A');    // 将二十六进制转换为十进制 
          }
          
          return id;
      } 
  • 如果字符串中出现小写字母
    • 那么可以把 A~Z 视为 0~25 ,而把 a~z 视为 26~51 
    • 就变成了五十二进制转换为十进制的问题
      int hashFunc1(char S[], int len) {    // hash 函数,将字符串 S 转换为整数 
          int id = 0;
          for(int i=0; i<len; ++i) {
              if(S[i] >= 'A' && S[i] <= 'Z') {        // 大写字母 
                  id = id*52 + (S[i] -'A');
              } else if(S[i] >= 'a' && S[i] <= 'z') {    // 小写字母 
                  id = id*52 + (S[i] -'a') + 26;
              }
          }
          
          return id;
      } 
  • 而如果出现了数字,一般有两种处理方法:
    1. 按照小写字母的处理方法,增大进制数至62
    2. 如果保证在字符串的末尾是确定个数的数字,那么就可以把前面的英文字母的部分按上面的思路转换成整数,再将末尾的数字直接拼接上去。例如对由三个字符加一位数字组成的字符串 ”BCD4“ 来说,就可以先将前面的 ”BCD“ 转换为整数 731 ,然后直接拼接上末位4变为7314即可
      int hashFunc2(char S[], int len) {    // hash 函数,将字符串 S 转换为整数 
          int id = 0;
          for(int i=0; i<len-1; ++i) {
              id = id*26 + (S[i]-'A');    // 将二十六进制转换为十进制 
          }
          id = id*10 + (S[len-1] - '0');    // 拼接末位 
          
          return id;
      }
  • 以一个问题结尾:给出 N 个字符串(由恰好三位大写字母组成),再给出 M 个查询字符串,问每个查询字符串在N个字符串中出现的次数
    • 思路:将字符串转换为整数,然后利用上面的整数散列解决该问题
      #include <cstdio>
      #include <string>
      #include <algorithm>
      #include <cmath>
      using namespace std;
      
      const  int maxn = 100;
      char S[maxn][5], temp[5];
      int hashTable[26*26*26 + 10] = {0};
      
      int hashFunc(char S[], int len) {
          int id = 0;
          for(int i=0; i<len; ++i) {
              id = id*26 + (S[i]-'A');
          }
          
          return id;
      } 
      
      int main() {
          int n, m;
          scanf("%d%d", &n, &m);
          
          for(int i=0; i<n; ++i) {
              scanf("%s", S[i]);
              int id = hashFunc(S[i], 3);        // 将字符串转换为整数 
              hashTable[id]++;                // 该字符串出现次数+1 
          }
          for(int i=0; i<m; ++i) {
              scanf("%s", temp);
              int id = hashFunc(temp, 3);        // 将字符串 temp 转换为整数 
              printf("%d\n", hashTable[id]);    // 输出该字符串出现次数 
          }
      
          return 0;
      }

       

 

posted @ 2018-01-09 11:32  Just_for_Myself  阅读(399)  评论(0编辑  收藏  举报