hash與字串hash入門
一.hash的引入.
考慮一個問題,給定n( )個範圍在 內的數,要求快速查詢x是否在這n個數中.
直接桶就MLE了,但是沒關係我們可以直接上平衡樹,或者直接STL裡的set或者map就好了,甚至你可以給n個數排個序再二分查詢…
但是如果要求
查詢上述演算法就都沒了…這個時候hash的優勢就體現出來了.
二.hash表.
hash表是啥?其實也是個桶,好吧其實桶就是一種最簡單的hash表.
我們考慮桶個實現原理,桶其實就是開了一個數組 表示 是否存在.然後我們思考對於上面這個問題,發現開 個桶也太浪費了,平均要 個位置才有一個數.
所以我們考慮讓設一個函式 ,考慮讓 存x,然後讓 的值域變得比較小,就可以省下很多記憶體了.
現在考慮如何設定 ,最簡單的當然是讓 ,其中M是任意一個常數.不過事實證明當M為一個質數時就很少會出現 衝突的情況了,所以M一般取一個n的4~5倍的大質數.
但是衝突出現得再少照樣會有,所以我們考慮如何處理衝突,一般有兩種方案:
1.給每一個位置存一個連結串列,表示
為當前位置的所有數.
2.當插入
衝突時,就往
的位置放(其中k是一個比較小的常數,事實證明k取3,5,7會比較優秀),如果還是衝突就嘗試
放…直到沒有衝突.
一般情況下本人比較喜歡用第2種(第2中只要寫個迴圈就好了,第1種要寫連結串列誒),所以我們這裡只提供第2種寫法的插入與查詢:
struct Hash_table{
int h[M+9],v[M+9]; //v[x]是存在第x個位置的數的真實值
void add(int &a,const int &b){a+=b;if (a>=M) a-=M;}
void insert(int x){ //插入一個數
int t=x;
for (x%=M;h[x]&&v[x]^t;add(x,5));
++h[x];v[x]=t;
}
bool find(int x){ //查詢一個數
int t=x;
for (x%=M;h[x]&&v[x]^t;add(x,5));
return h[x]?1:0;
}
}
三.字串hash.
我們考慮一些複雜的資訊,例如字串上的一些資訊給如何運用hash表來處理.
很容易想到我們可以把字串看成一個P進位制的數,第一個字元是最高位,第二個字元是第二高位…這樣每個字串我們都可以看成一個數字了,接下來我們設一個字串s對應的P進位制數為 .
但是這個數字太大怎麼辦?我們可以直接講這個數字對一個數取模,這個數通常可以取 ,這樣就可以直接用unsigned long long來存減少取模運算了,而且這樣子通常不會被卡.在這個情況下可以讓P等於131或者13331,事實證明這兩個數產生衝突的情況較小.
再來考慮在這樣的字串hash的處理下,對於兩個串
和
,如果我們知道
和
對應的P進位制數
與
,那麼t對應的P進位制數就可以這樣求:
那麼我們要求一個串 的一個子串 對應的P進位制數就可以通過預處理字首和來搞了.
求一個串s[l…r]對應的P進位制數就可以這樣寫:
ULL Hash(ULL *a,int l,int r){return a[r]-a[l-1]*Pow[r-l+1];} //其中a陣列是字串s對應的字首和
四.例題與程式碼.
這裡就只給一道字串hash的例題.
題目:BZOJ4779.
這道題只需要二分一下可行的長度len就可以用字串hash過啦(一開始我還天真的認為 能過…).
程式碼如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
typedef unsigned long long ULL;
char rc(){
char c=getchar();
while (c<'A'||c>'Z') c=getchar();
return c;
}
const int N=500;
const ULL P=131,M=5000081;
ULL Pow[N+9];
ULL Hash(ULL *a,int l,int r){return a[r]-a[l-1]*Pow[r-l+1];}
struct Hash_table{
int h[M+9];
ULL v[M+9];
void add(ULL &a,const ULL &b){a+=b;if (a>=M) a-=M;}
bool find(ULL x){
ULL t=x;
for (x%=M;h[x]&&v[x]^t;add(x,5));
return h[x]?1:0;
}
void insert(ULL x){
ULL t=x;
for (x%=M;h[x]&&v[x]^t;add(x,5));
++h[x];v[x]=t;
}
void erase(ULL x){
ULL t=x;
for (x%=M;h[x]&&v[x]^t;add(x,5));
--h[x];
if (h[x]<0) h[x]=0;
}
}h;
int n,m,ans;
ULL s1[N+9][N+9],s2[N+9][N+9];
bool check(int len){
int r,flag;
for (int l=1;l+len-1<=m;++l){
r=l+len-1;flag=0;
for (int i=1;i<=n;++i)
h.insert(Hash(s1[i],l,r));
for (int i=1;i<=n;++i)
if (h.find(Hash(s2[i],l,r))) flag=1;
for (int i=1;i<=n;++i)
h.erase(Hash(s1[i],l,r));
if (!flag) return true;
}
return false;
}
Abigail into(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
s1[i][j]=s1[i][j-1]*P+rc();
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
s2[i][j]=s2[i][j-1]*P+rc();
}
Abigail work(){
Pow[0]=1;
for (int i=1;i<=m;++i)
Pow[i]=Pow[i-1]*P;
int l=1,r=m;
for (int mid=l+r>>1;l+1<r;mid=l+r>>1)
check(mid)?r=mid:l=mid;
ans=check(l)?l:r;
}
Abigail outo(){
printf("%d\n",ans);
}
int main(){
into();
work();
outo();
return 0;
}