什么是Hash
Hash就像是一个桶排,那只不过是把各个元素的数值当做下标进行存储.其最常用的用途就是用来判重.但是,如何对字符串进行判重,不可能一个一个往前超,若n上万则显然不可行.我们可以选择进行Hash,将每一个字符串或者大数字进行一定的操作即可进行.
对大整数类型进行Hash
取模法
对于每一个大整数进行取模,即除以一个大质数(例如107,10007,1000007,1-奇数个0-7),这样就作为数组的下标进行存储了.
为什么要对一个大整数取模
emmmmmm......经众多数学家证明重复的几率较小
万一实在有不可避免的误差/出题人卡你怎么办
有两种方法:
1.线性探测开拓地址法
2.拉链法
对字符串进行Hash
对字符串,我们可以选择按权展开法进行实现.
例如:对于字符abc,我们可以转换成26进制,即:126^0+226^1+3*26^2,很好理解吧
例如,对于字符串3gT,我们可以转换成某个质数进制即可.
但是,不要忘了,在按权展开的时候需要对一个大整数取模
如何避免字符串Hash的误差
同样:
1.线性探测开拓地址法
2.拉链法
线性探测开拓地址法
若hash[k]已经被填入,则尝试填写hash[k+1],hash[k+2]....等数组.最后在查找的时候也逐一查找即可.但毕竟查找不方便,个人更加偏向于拉链发
拉链法
跟据每一个数值的下标扩充一个链表,每次在链表上逐一查找即可.
为了方便实现,我们可以选用STL中的vector(即动态数组或不定长数组)来代替链表,这样也十分容易实现.
整数Hash实例:不重复数字【JLOI2011】
题目描述
给出N个数,要求把其中重复的去掉,只保留第一次出现的数。
例如,给出的数为1 2 18 3 3 19 2 3 6 5 4,其中2和3有重复,去除后的结果为1 2 18 3 19 6 5 4。
输入输出格式
输入格式:
输入第一行为正整数T,表示有T组数据。
接下来每组数据包括两行,第一行为正整数N,表示有N个数。第二行为要去重的N个正整数。
输出格式:
对于每组数据,输出一行,为去重后剩下的数字,数字之间用一个空格隔开。
(原题体面戳这里)
对于这道题,直接除以大质数1000007即可.(数据水,没有判重AC了)
代码如下:
#include<bits/stdc++.h>
using namespace std;
int Hash[1000009];
inline int h(int x){return x%1000007;}
void work()
{
memset(Hash,0,sizeof(Hash));
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
if (Hash[h(x)]==1) continue;
}
printf("\n");
}
int main()
{
int T;
scanf("%d",&T);
while (T--) work();
}
字符串Hash
这道题,我们采用301当做位权;我们采用大质数1000007来取模;用拉链法来处理重复;同时用vector来代替链表;代码实现难度不大.
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,ans=0;
int const M=1000007;//最后取模
vector<string>Link[1000009];
inline int hash(string x)
{
int sum=0;int w=307;//累加的值,位权
for (int i=0;i<x.length();i++)
sum=(sum*w+int(x[i]))%M;
return sum;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for (int i=1;i<=n;i++)
{
string s;cin>>s;
int k=hash(s),flag=1;
for (int j=0;j<Link[k].size();j++)
if (s==Link[k][j]) flag=0;
if (flag==1)
{
Link[k].push_back(s);
ans++;
}
}
cout<<ans;
return 0;
}
原文地址:https://www.cnblogs.com/pigzhouyb/p/10119826.html