字符串hash思想的來源(個人猜測):
對於一系列的字符串,如果我們需要重復比較它們是否相同的話,顯然這會是一個接近於O(N^3)的算法,時間復雜度太高了。於是我們期望得到一個更好的算法。
把目光着眼到我們的十進制數字,也就是我們平時使用的數字上面。我們很容易發現,比較兩個數字的時間復雜度是O(1)的。而數字本質上也是一種字符串,也就是說數字的比較了我們一種啟示。
舉個例子,假如說給出一個數字構成的字符串,那么我們只需要把這個字符串轉化成一個數字,就可以通過O(1)的時間比較這兩個字符串是否相同。當然,這個說法是錯誤的,當數字構成的字符串長度非常大的時候,就算存在這樣一種方法可以儲存數字,比較兩個數字是否相同所需要花費的代價肯定會隨着字符串的加長而增大。
回到字符串上面。假設接下來只討論字符串中只包含小寫的英文字母,那么一個字符串實際上可以看成是一個26進制的數字(事實上只要進制大等於於26就可以取得相當好的理論效果)。當然就像我們前面說的,這個“數字”無法儲存。但是考慮一下我們一般處理的小規模問題,字符串的數量可能大概也就在1e3到1e5之間,也就是說假如用一個數字代表一個字符串,那么就連int型的整數用來儲存這些數字都綽綽有余。而作為一個事實,我們無法儲存一個字符串對應的完整的26進制。但是假如我們可以讓這個字符串的末8位幾乎不重復(實際上一般來說是在一個同余系中不重復,末八位是一個特例),就可以起到一個數字代表一個唯一字符串的效果,並且可以在O(1)時間內完成一對較長的字符串是否相同的對比。
上面的思想所反映出來的字符串比較做法就是字符串hash。一個字符串對應的數字就叫做這個字符串的hash值。顯然,就像上面提到的,有可能出現兩個字符串對應的hash值相同的情況。為了避免這樣的情況,人們往往會把同余系的模數取成一個很大的質數,並且把hash值的值域放大到long long,甚至還有雙hash(即一個字符串有兩個在不同參數下得到的hash值,當兩個字符串的這兩個hash值一模一樣的時候才認為這兩個字符串相同)這樣的做法。
算法適用范圍:
hash適用於已知字符串集(字符串不一定要完全確定,如果它在確定范圍內發生變化,當然可也以認為字符串是已知的),需要大量比較字符串是否相同時。通過線性時間預處理,就可以用O(1)的時間判斷任意兩個字符串是否相同。
算法實現:
個人的一般做法,hash值定義為long long型,模數取1e15+7,把字符串看成29進制(質數越多越好)。
給出一段代碼(默認字符串中只有小寫字母):

1 typedef long long LL; 2 const int maxn=10005; 3 const LL mo=1e15+7; 4 5 LL gethash(char *s){ 6 int n=strlen(s); LL tmp=1,re=0; 7 for(int i=0;i<n;i++){ 8 re=(re+tmp*(s[i]-'a'))%mo; 9 tmp=tmp*29%mo; 10 } 11 return re; 12 }