Hash,通常翻譯作散列、雜湊,或音譯爲哈希。————摘自百度百科
先來看個題:給你一坨一些鍵值集<key,value>
,\(key\)的範圍是\([1,10^{10}]\),每次詢問\(x\),回答\(key=x\)的\(value\)這種一看就知道暴力不行……因而,有些同窗會說:我會用map
!但map
的查詢是 \(O(logn)\)的 QwQ。那麼哈希能夠怎麼作呢?咱們可讓\(hash[f(key)]=value\),其中\(f()\)函數被稱爲哈希函數。至於\(f()\)函數怎麼寫……想怎麼寫就怎麼寫!沒錯,你想怎麼寫就怎麼寫。通常有這麼幾種方法:ios
那麼如今上面那個問題就好解決了,只要用取餘數法求得\(key\)的哈希地址就能夠大大壓縮空間了!
可是,你不要高興太早!相信有許多人已經看出來了,哈希的缺點很明顯,就是容易出現不一樣的元素有同一個哈希地址的狀況,咱們通常稱其爲:哈希衝突。那麼有什麼方法能解決哈希衝突呢?有許多方法:函數
0 0 0 34 6 44 0 0
注:0表示沒有元素。而後又有一個元素\(8\),獲得其哈希地址也是4(即34所在的位置),那麼咱們就日後挪一挪:大哥你先來的,我到後面去。因而來到了6的位置——也被佔了,那麼再日後移……最後到了7(即44後面那個)。而後查詢時只要依次日後找就能夠了。解決哈希衝突的方法通常就是這些啦!還有個問題,上面提到模數要是質數,爲何呢?緣由很簡單,根據質數的特性,質數每個位置都能很好的利用起來,而合數不能夠。並且這個質數要大一點(廢話,你來個19,玩個鬼哦)。
好,講完了基礎的,來看一看例題:
P3370 【模板】字符串哈希
噫,剛剛只說了整數哈希啊!不要緊,記得ASCII碼嗎?咱們能夠經過ASCII碼,將其轉成一個\(base\)進制數,固然,是模過的。而後再用鏈地址法,對同一哈希值的字符串進行遍歷,若是都不相同,加入並更新答案。
具體代碼實現:spa
#include<cstdio> #include<string> #include<vector> #include<iostream> #define mod 23333 #define base 298 #define rg register using namespace std; int n,ans; string s; vector<string>v[mod+5]; void insert() { int hash=1;//記錄哈希值,因爲後面要乘因此初值是1 for(rg int i=0;i<s.length();i++) hash=(1ll*hash*base+s[i])%mod;//1ll就是(long long)1,乘一個1ll,能夠保證不爆精度(固然你爆long long或高精度我也沒辦法) string t=s;//暫存一下 for(rg int i=0;i<v[hash].size();i++) if(v[hash][i]==t) return ;//判斷,若是有相同的就退出 v[hash].push_back(t);//加入新的字符串 ans++;//更新答案 return ; } int main() { scanf("%d",&n); for(rg int i=1;i<=n;i++) { cin>>s; insert(); } printf("%d",ans); return 0; }
哦對了,通常233333(2後面跟一堆3)、100007(1和7中間隔一堆0)、1000009(1和9中間隔一堆0)都是質數。翻譯
就講這麼多吧,以後就要靠你們本身實現了!重點仍是在多刷題啊!code