雜湊表的簡單實現
雜湊,一種簡單的演算法,通過對字元型別的ASCLL碼值進行運算並取模得到雜湊值,然後對於每一個雜湊值開一個空間,將他們串到一起,也就是說分到一類。
這種演算法常被用來加密,比如使用者的密碼。一些著名的例子如名字競技場的MD5。
當用戶設定初始密碼後,計算機經過驗算,將其轉化為一個值,並儲存下來,日後使用者想要登陸的時候就對使用者輸入的密碼進行運算,進行匹配,如果值相同則通過。
一個求雜湊值簡單的實現:
int hash1(char s[]){ unsigned long long k;//用於儲存雜湊值 int len=strlen(s);//字串的長度 for(int i=1;i<=len;++i){ k+=s[i]*s[i];//不用擔心溢位,unsigned型別必為整數,當他超過最大值時就會自動模最大值 } return k%mod;//返回 }
在這裡,我們看到雜湊的一個簡單的實現方法。其中,mod是一個變數,可以根據自己的意願進行更改,當然,數字越大匹配的精確度就越高。
這時就有朋友想問了:如果剛好取模後撞上了怎麼辦?不用擔心,我們可以多寫幾個函式,以避免撞車。
當然無論怎麼寫總是會有一些字串的雜湊值相同,這是無可避免的,只能儘量縮小。
方才我們列舉的那個例子是有缺陷的,無法判斷順序不同的字串。
這時我們就要運用一種名曰加權的方法。對,就是那個轉化進位制時用到的加權:對於每一個位置上的數進行加權,以區分位置的區別。
比如下面的又一個例子:
int hash2(char s[],int tmp){ unsigned long long k; int len=strlen(s); for(int i=1;i<=len;++i){ k=h*tmp+s[i];//加權 } return k%mod; }
其中的tmp便是位權,這個值可以隨意更改。
這樣我們就可以區分順序不同的字串了。
那麼,在求得雜湊值後,我們該如何儲存這些雜湊值呢?
這時候,我們就會用到雜湊表了。而雜湊表的一種實現方式——
噹噹噹,單鏈表閃亮登場!
我們將每一個雜湊值進行分類,雜湊值相同的,串到同一條鏈上,這樣便於儲存,也節省空間。
單鏈表的具體實現如下:
struct data{
int key;
struct data *next;
};
我們用next這個指標指向這個元素所串著的下一個元素,key就是雜湊值。
那麼,搞定了雜湊表的基本問題,讓我們來做一題練練手吧。
問題 : 生成字串
題目描述
假設字串只由字元‘0’,‘1’,‘*’組成,其中字元‘*’表示該字元可由字元‘0’或‘1’替代。
現有一些字串,根據這些字串生成所有可生成的字串。如:
{10,*1,0* }可生成{10,01,11,00 }
{101,001,*01}可生成{101,001}
注意後一個例子中‘*01’並沒有生成新的字串。
輸入
第一行是兩個整數m,n。(1≤m≤15,1≤n≤2500)m表示字串的長度,n表示字串的個數。兩個整數之間由一個空格隔開。以下n行每行各有一個字串。檔案中各行的行首、行末沒有多餘的空格。
輸出
輸出檔案只有一個整數,表示所能生成的字串的個數。
樣例輸入
2 3
10
*1
0*
樣例輸出
4
程式碼實現:
#include<bits/stdc++.h>
using namespace std;
#define zero '0'
#define one '1'
string s[2333];
int n,m;
int f[23333];
char s1[23333];
int maxn;
void _hash(){
int er=1;
for(int i=0;i<m-1;i++)er*=2;
int su=0;
for(int i=0;i<m;i++){
int bp=s1[i]-'0';
su+=bp*er;
er/=2;
}
if(f[su]==0){
f[su]=1;
maxn++;
}
}
void make(string st,int t){
if(st[t]=='*'){
s1[t]=zero;
if(t==m-1)_hash();
else make(st,t+1);
s1[t]=one;
if(t==m-1)_hash();
else make(st,t+1);
}
else{
s1[t]=st[t];
if(t==m-1)_hash();
else make(st,t+1);
}
}
int main(){
cin>>m>>n;
for(int i=0;i<n;i++)cin>>s[i];
for(int i=0;i<n;i++)make(s[i],0);
cout<<maxn;
}