hash雜湊
字串雜湊就是將字串轉化為一個整數
Hash方法
給定一個字串 \(s=s_1,s_2,s_3 ... s_n\) ,
對字母\(x\),我們規定 \(idx(x)=x−′a′+1\)。
(當然也可以直接用 \(s_i\)的 ASCII值)
1.自然溢位方法
\[hash[i]=hash[i−1]∗p+idx(s[i]) \]利用 \(ull\) 的自然溢位,對其取模
2.單hash方法
\(hash\)公式:
\[hash[i]=(hash[i-1]*p+idx(s[i])\% mod \]\(p\) 和 \(mod\) 均為質數,且 \(p<mod\) (儘量取大)
假設 \(s=abc,p=13,mod =101\)
hash[0]=1;
hash[1]=(hash[0]*13+2)%101=15;
hash[2]=(hash[1]*13+3)%101=97;
\(abc=97\) , \(97\) 即為 \(abc\) 雜湊值;
3.雙Hash方法
將一個字串用不同數值的 \(mod\) \(hash\) 兩次,將這兩個結果用一個二元組表示,作為 \(Hash\) 結果。
\(Hash\) 公式
\[hash1[i]=(hash1[i−1])∗p+idx(s[i]) \% mod1 \]\[hash2[i]=(hash2[i−1])∗p+idx(s[i]) \% mod2 \]用\(map\)儲存結果為 \(<hash1[n],hash2[n]>\)
這種\(hash\)很安全。
獲取子串的雜湊
若已知一個 \(|s|=n\) 的字串的 \(hash\) 值,\(hash[i],1≤i≤n\) 。
其子串
\([S_l..S_r],1≤l≤r≤n\) 對應的 \(hash=f(r-l)\) 值為:
考慮到 \(hash[i]\) 每次對 \(p\) 取模,進一步得到下面的式子:
\[f(r-l)=(hash[r]−hash[l−1]∗p^{r−l+1})\%mod \]括號裡面是減法,即有可能是負數,故做如下的修正:
\[f(r-l)=((hash[r]−hash[l−1]∗p^{r−l+1})\%mod+mod)\%mod \]至此得到求子串 \(hash\)
我們可以預先求出來 \(p^{\sum^n_1}\) 的各項值,直接求解即可。
接下來是可選擇的hash係數
例題
CF1200E Compress Words
題意:
給定幾個字串,按順序求對於他們的最小表達,例如 want to ,最小表達為 wanto
解法:
看到這個題,我們可以快速得出來一個解決方法,比較前字串的字尾和後字串的字首,如果相同,則不將後字串的相同部分進入答案,如果不同,那麼就加進去答案。
用雙 \(hash\) 處理,對於答案序列設 \(h1[n],h2[n]\) ,當前序列為 \(hash1,hash2\)
那麼此時序列成立的條件是:
\[h1[cnt]=(hash1+h1[cnt-i]*p1[i])\%mod1 \]\[h2[cnt]=(hash2+h2[cnt-i]*p2[i])\%mod2 \]如果成立,就把當前的i進行記錄 \(L=i\)
向答案序列中加入的就是序列 \(s[L-len]\)。
程式碼:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+6,mod1=19260817,mod2=19491001;
ll p1,p2;
int n,cnt=0;
ll P1[N],P2[N],h1[N],h2[N];
char s[N],ans[N];
void init(){
srand(time(0));
p1=rand()%321+233,p2=rand()%233+321;
P1[0]=P2[0]=1;
for(int i=1;i<=N-6;i++) P1[i]=(ll)P1[i-1]*p1%mod1,P2[i]=(ll)P2[i-1]*p2%mod2;//cout<<P1[i]<<" ";
}
signed main()
{
cin>>n;
init();
for(int T=1;T<=n;T++){
scanf("%s",s+1);
int len=strlen(s+1);
int hash1=0,hash2=0,L=0;
for(int i=1;i<=len&&i<=cnt;i++){
hash1=((ll)hash1*p1+s[i])%mod1;
hash2=((ll)hash2*p2+s[i])%mod2;
if(h1[cnt]==(hash1+(ll)h1[cnt-i]*P1[i])%mod1&&
h2[cnt]==(hash2+(ll)h2[cnt-i]*P2[i])%mod2)
L=i;
}
for(ll i=L+1;i<=len;i++){
ans[++cnt]=s[i];
h1[cnt]=((ll)h1[cnt-1]*p1+s[i])%mod1;
h2[cnt]=((ll)h2[cnt-1]*p2+s[i])%mod2;
}
}
printf("%s",ans+1);
//system("pause");
return 0;
}