[toc]
# 以下內容作廢,太多錯誤了,等我有時間重寫
說一下什么是Hash,說白了就是把一大坨字符用一些神奇的數來表示,可以說是把字符加密了.
簡單一點就是一個像函數一樣的東西,你放進去一個值,它給你輸出來一個值。輸出的值就是Hash值。一般Hash值會比原來的值更好儲存(更小)或比較。
字符串hash的靈魂就是盡量讓不同的字符串對應唯一的hsah的值 .而要實現這一效果就要選對方法否則就咕咕咕了
舉個栗子:
如果我們的加密方法是把字符的ascal加起來,那就咕咕咕了.
比如:
ababa
babaa
加起來是一樣的,咕咕咕....
所以應該怎么hash?
用一種名為“BKDR Hash”的字符串Hash算法:
它的主要思路是選取恰當的進制,可以把字符串中的字符看成一個大數字中的每一位數字,不過比較字符串和比較大數字的復雜度並沒有什么區別(高精數的比較也是O(n)O(n)的),但只要把它對一個數取模,然后認為取模后的結果相等原數就相等,那么就可以在一定的錯誤率的基礎上O(1)O(1)進行判斷了。
那么我們選擇什么進制比較好?
首先不要把任意字符對應到數字0,比如假如把a對應到數字0,那么將不能只從Hash結果上區分ab和b(雖然可以額外判斷字符串長度,但不把任意字符對應到數字0更加省事且沒有任何副作用),一般而言,把a-z對應到數字1-26比較合適。
關於進制的選擇實際上非常自由,大於所有字符對應的數字的最大值,不要含有模數的質因子(那還模什么),比如一個字符集是a到z的題目,選擇27、233、19260817都是可以的。
模數的選擇(盡量還是要選擇質數):
絕大多數情況下,不要選擇一個109109級別的數,因為這樣隨機數據都會有Hash沖突,根據生日悖論,隨便找上109−−−√109個串就有大概率出現至少一對Hash 值相等的串
最穩妥的辦法是選擇兩個109109級別的質數,只有模這兩個數都相等才判斷相等,但常數略大,代碼相對難寫,目前暫時沒有辦法卡掉這種寫法(除了卡時間讓它超時)
如果能背過或在考場上找出一個10181018級別的質數(Miller-Rabin),也相對靠譜,主要用於前一種擔心會超時,后一種擔心被卡。
偷懶的寫法就是直接使用unsigned long long,不手動進行取模,它溢出時會自動對264
送上一道模板題:
P3370 【模板】字符串哈希
題目描述
如題,給定N個字符串(第i個字符串長度為Mi,字符串內包含數字、大小寫字母,大小寫敏感),請求出N個字符串中共有多少個不同的字符串。
輸入輸出格式
輸入格式:
第一行包含一個整數N,為字符串的個數。
接下來N行每行包含一個字符串,為所提供的字符串。
輸出格式:
輸出包含一行,包含一個整數,為不同的字符串個數。
輸入輸出樣例
說明
時空限制:1000ms,128M
數據規模:
對於30%的數據:N<=10,Mi≈6,Mmax<=15;
對於70%的數據:N<=1000,Mi≈100,Mmax<=150
對於100%的數據:N<=10000,Mi≈1000,Mmax<=1500
代碼:
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<queue> #include<stack> #include<vector> #include<map> #include<string> #include<cstring> #define ll long long int #define mod 1000000007 using namespace std; const long long int maxn=99999999999999; const int minn=-999999999; long long base=131; long long a[15010]; char s[15010]; int n,ans=1; long long hash(char s[]) { int len=strlen(s); long long ans=0; for (int i=0; i<len; i++) ans=ans*base+(long long )s[i]; return ans%maxn; } int main() { scanf("%d",&n); for (int i=1; i<=n; i++) { scanf("%s",s); a[i]=hash(s); } sort(a+1,a+n+1); for (int i=2; i<=n; i++) if (a[i]!=a[i-1]) ans++; printf("%d\n",ans); }
送上取膜數對答案的影響:
.
改了一下mod為0x3f3f3f就