1. 程式人生 > 其它 >hash雜湊

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)\) 值為:

\[f(r-l)=hash[r]−hash[l−1]∗p^{p-l+1} \]

考慮到 \(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;
}