算法初步——散列
散列的定义与整数散列
- 题目要求: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; }
- 思路:设定一个int型数组 hashTable[10010] ,然后在输入 N 个数时进行预处理,即当输入的数为 x 时 ,就令 hashTable[x]++,时间复杂度为 O(N+M)
- 但是这个策略暂时还有一个问题——如果输入可能是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; }
- 而如果出现了数字,一般有两种处理方法:
- 按照小写字母的处理方法,增大进制数至62
- 如果保证在字符串的末尾是确定个数的数字,那么就可以把前面的英文字母的部分按上面的思路转换成整数,再将末尾的数字直接拼接上去。例如对由三个字符加一位数字组成的字符串 ”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; }
- 思路:将字符串转换为整数,然后利用上面的整数散列解决该问题