一、字符串哈希简介
Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
字符串哈希即当字符串过长时若逐位比较时间开销会很大,这时通过哈希算法求出它的哈希值,利用哈希值进行比较即可减小时间开销。这里要介绍的是最常用的一种哈希算法:进制哈希。进制哈希先给出一个数即进制,将一个串的每一个元素看做一个进制位上的数字,然后求出这个串所对应的数,这个数就是这个串的哈希值,通过比较不同串的的哈希值来判断两个串是否相同,通过这种方法可以大大减少时间复杂度。
二、模板题目-字符串哈希
这道题是一道模板题,很经典,但是很能帮助我们理解哈希以及明白哈希的作用。
题目内容
如题,给定 N 个字符串(第 i个字符串长度为 M_i,字符串内包含数字、大小写字母,大小写敏感),请求出 N个字符串中共有多少个不同的字符串。
输入格式
第一行包含一个整数 N,为字符串的个数。
接下来 N 行每行包含一个字符串,为所提供的字符串。
输出格式
输出包含一行,包含一个整数,为不同的字符串个数。
输入输出样例
输入
5
abc
aaaa
abc
abcc
12345
输出
4
说明/提示
对于30% 的数据:N≤10,M_i≈6,Mmax≤15。
对于 70%70% 的数据:N≤1000,M_i≈100,Mmax≤150。
对于 100%100% 的数据:N≤10000,M_i≈1000,Mmax≤1500。
解题过程
先放上常用模板(出自ACM学长-OHAMEOW队伍),模板中的131即是前文所提的进制哈希中的进制,MOD选择一个较大的数(最好是大质数) :
ll BKDR_hash(char *str) {
ll ans = 0;
int len = strlen(str);
for (int i = 0; i < len; i++) {
ans = (ans * 131 + str[i]) % MOD;
// 种子可以是 31、131、1313、13131、131313..
}
return ans;
}
计算每一个字符串的哈希值并存储在数组中,计算完毕后利用sort进行排序,再找出有多少个不重复的字符串:
sort(hashValue,hashValue+n);
int ans = 1;
for(int i=1;i<n;i++){
if(hashValue[i]!=hashValue[i-1]) ans++;
}
全部代码
#include<bits/stdc++.h>
#define MOD 2031111029377612937
using namespace std;
long long int hashValue[10005];
long long int BKDR_hash(string str){
long long int ans = 0;
int len = str.length();
for(int i=0;i<len;i++){
ans = (ans*131 + str[i]) % MOD;
}
return ans;
}
int main(){
int n;
cin >> n;
string s;
for(int i=0;i<n;i++){
cin >> s;
hashValue[i] = BKDR_hash(s);
}
sort(hashValue,hashValue+n);
int ans = 1;
for(int i=1;i<n;i++){
if(hashValue[i]!=hashValue[i-1]) ans++;
}
cout << ans;
}