哈希,hash

Hash,通常翻譯作散列、雜湊,或音譯爲哈希。————摘自百度百科
先來看個題:給你一坨一些鍵值集<key,value>,\(key\)的範圍是\([1,10^{10}]\),每次詢問\(x\),回答\(key=x\)\(value\)這種一看就知道暴力不行……因而,有些同窗會說:我會用map!但map的查詢是 \(O(logn)\)的 QwQ。那麼哈希能夠怎麼作呢?咱們可讓\(hash[f(key)]=value\),其中\(f()\)函數被稱爲哈希函數。至於\(f()\)函數怎麼寫……想怎麼寫就怎麼寫!沒錯,你想怎麼寫就怎麼寫。通常有這麼幾種方法:ios

  1. 直接定值法:讓\(F(key)=key\)。讀者:那不和通常的同樣嘛!要你何用?咳咳,總要從最基礎的開始嘛……好了,這個方法就此結束。
  2. 摺疊法:將關鍵字分紅幾部分,取這幾部分的和的前幾位爲哈希地址。例如ISBN碼(對就是那道入門題),以0-442-20586-4爲例,能夠獲得其哈希地址是:\(hash = 0442 + 2058 + 64 = 2564\)誒?以前不是說前幾位嗎?由於這裏沒有進位啊……若是獲得結果是\(10934\),那麼其哈希地址就是\(0934\)
  3. 取餘數法:這是\(OI\)中最最經常使用的,就是對其求餘爲哈希地址,如\(4325\),模\(233333\)後得\(4325\),那麼她的哈希地址就是\(4325\)

那麼如今上面那個問題就好解決了,只要用取餘數法求得\(key\)的哈希地址就能夠大大壓縮空間了!
可是,你不要高興太早!相信有許多人已經看出來了,哈希的缺點很明顯,就是容易出現不一樣的元素有同一個哈希地址的狀況,咱們通常稱其爲:哈希衝突。那麼有什麼方法能解決哈希衝突呢?有許多方法:函數

  1. 線性探測再散列:若是多個數有同一個哈希地址,如:0 0 0 34 6 44 0 0注:0表示沒有元素。而後又有一個元素\(8\),獲得其哈希地址也是4(即34所在的位置),那麼咱們就日後挪一挪:大哥你先來的,我到後面去。因而來到了6的位置——也被佔了,那麼再日後移……最後到了7(即44後面那個)。而後查詢時只要依次日後找就能夠了。
  2. 多重哈希:我的比較喜歡叫雙模數,就是進行屢次哈希,固然,每次的模數都不同。若是兩次哈希的地址都對應,我才認爲是同一個。通常狀況下雙模數就足夠了,因此我喜歡叫雙模數。要是有三模數,那基本不會衝突了。
  3. 鏈地址法:還記得鏈式前向星嗎?(不記得!那還不滾回去學)鏈式前向星就是把其後面一個一個地鏈起來,這個也同樣,若是哈希地址相同,就鏈起來。因而乎,咱們又想:那開個\(vector\)豈不是又方便又能夠解決哈希衝突?沒錯,我也喜歡用\(vector\)。這兩種你用哪種都不要緊,不過仍是先提醒一下:\(vector\)有可能會爆哦!這也是爲何你們用鏈式前向星而不用\(vector\)的緣由!(因此我也要開始習慣用鏈地址法了)。
  4. 創建一個公共溢出區:這個方法我不會!(不會還義正詞嚴……)因此這裏不講,並且這個方法最後獲得的模數不是一個質數,並且其因數不少,是2的次冪,因此……出題人卡你是很容易的!

解決哈希衝突的方法通常就是這些啦!還有個問題,上面提到模數要是質數,爲何呢?緣由很簡單,根據質數的特性,質數每個位置都能很好的利用起來,而合數不能夠。並且這個質數要大一點(廢話,你來個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

相關文章
相關標籤/搜索