字符串哈希学习入门+一些练习题目

概念介绍

字符串哈希实质上就是把每个不同的字符串转成不同的整数
目的:判断一个字符串是否出现过

基本哈希方法

1.自然溢出
利用unsigned long long的自然溢出:
当数据大于范围时,会自动 m o d ( 2 64 − 1 ) mod (2^{64}-1) mod(2641)
这样就不用mod质数来保证唯一性了。
Hash公式

h a s h [ i ] = h a s h [ i − 1 ] ∗ p + i d ( s [ i ] ) hash[i]=hash[i-1]*p+id(s[i]) hash[i]=hash[i1]p+id(s[i])
此处p要为质数。

2.单Hash法
(作用不大,略过)
3.双Hash法
对一个字符串用两个不同的哈希公式分别得到哈希值。
这样可以用一个数对 < h a s h 1 [ s ] , h a s h 2 [ s ] > <hash1[s],hash2[s]> <hash1[s],hash2[s]>来表示一个字符串的哈希值。
Hash公式

h a s h 1 [ i ] = ( h a s h 1 [ i − 1 ] ) ∗ p + i d ( s [ i ] ) hash1[i]=(hash1[i−1])∗p+id(s[i]) % mod1 hash1[i]=(hash1[i1])p+id(s[i])
h a s h 2 [ i ] = ( h a s h 2 [ i − 1 ] ) ∗ p + i d ( s [ i ] ) hash2[i]=(hash2[i−1])∗p+id(s[i]) % mod2 hash2[i]=(hash2[i1])p+id(s[i])

哈希素数的选择

大佬博客附有一些可选素数
在这里插入图片描述
左为error率,右为可选素数

1.入门模板题

传送门

#include <bits/stdc++.h>
using namespace std;
#define MAXN 202050
#define ll long long
int n,m,d;
typedef unsigned long long ull;
//#define _DEBUG
ull a[MAXN];
ull base=131;
int prime=233317;
ull mod=212370440130137957ll;
ull gethash(string s)
{
    
    
    int len=s.size();
    ull ans=0;
    for(int i=0;i<len;i++)
        ans=ans*base+ull(s[i])%mod+prime;
    return ans;
}
int main() {
    
    
#ifdef _DEBUG
    freopen("E://tt.txt","r",stdin);
#endif
    scanf("%d",&n);
    string s;
    for(int i=1;i<=n;i++)
    {
    
    
        cin>>s;
        a[i]=gethash(s);
    }
    sort(a+1,a+1+n);
    int cnt=1;
    for(int i=2;i<=n;i++)
    {
    
    
        if(a[i]!=a[i-1])cnt++;
    }
    cout<<cnt<<endl;
}

进阶部分

获取子串的哈希

作用:如果求出了一个字符串的Hash值,那么就可以o(1)求解字串哈希值。
公式:若已知一个 ∣ S ∣ = n |S|=n S=n的字符串的hash值.
h a s h [ i ] , 1 < = i < = n hash[i],1<=i<=n hash[i],1<=i<=n,其子串 s l , . . . . , s r sl,....,sr sl,....,sr, 1 < = l < = r < = n 1<=l<=r<=n 1<=l<=r<=n对应的Hash值为

h a s h = ( ( h a s h [ r ] − h a s h [ l − 1 ] ∗ p r − l + 1 ) % m o d + m o d ) % m o d hash=((hash[r]-hash[l-1]*p^{r-l+1})\%mod+mod)\%mod hash=((hash[r]hash[l1]prl+1)%mod+mod)%mod

进阶习题

传送门
用了双哈希还有一个模数。
(单哈希取模也可)
不取模的话被卡65了。。
解决方法是取1e9左右的大素数作为模数。
用米勒罗宾判素数即可找到一个。

#include <bits/stdc++.h>
 
using namespace std;
 
using ll = long long;
using ull=unsigned long long;
int n,m;
string t;
#define MAXN 1000500
ull p=	1610612741;
ull q=	805306457;
const ll mod=1000000223;
ull has[MAXN];
ull _has[MAXN];
ull sa[MAXN];
ull sb[MAXN];
void add(string s)
{
    
    
    ull now=0;
    ull _now=0;
    int lim=min(t.length(),s.length());
    int len=-1;
    for(int i=0;i<lim;i++)
    {
    
    
        now=(now*p%mod+s[i])%mod;
        _now=(_now*q%mod+s[i])%mod;
        ull cmp=(has[t.length()]-has[t.length()-1-i]*sa[i+1]%mod+mod)%mod;
        ull _cmp=(_has[t.length()]-_has[t.length()-1-i]*sb[i+1]%mod+mod)%mod;
        if(cmp==now&&_cmp==_now)
            len=i;
    }
    for(int i=len+1;i<(int)s.length();i++)
    {
    
    
        t+=s[i];
        has[t.length()]=(has[t.length()-1]*p%mod+s[i])%mod;
        _has[t.length()]=(_has[t.length()-1]*q%mod+s[i])%mod;
    }
}
int main() {
    
    
    //freopen("E://tt.txt","r",stdin);
    //ios::sync_with_stdio(false);
    srand(time(0));
    scanf("%d",&n);
 
    cin>>t;
    sa[0]=sb[0]=1;
    for(int i=1;i<MAXN;i++)
        sa[i]=sa[i-1]*p%mod,sb[i]=sb[i-1]*q%mod;
    for(int j=1;j<=t.length();j++)
    {
    
    
        has[j]=(has[j-1]*p%mod+t[j-1])%mod;
        _has[j]=(_has[j-1]*q%mod+t[j-1])%mod;
    }
    for(int i=1;i<n;i++)
    {
    
    
        string s;
        cin>>s;
        add(s);
    };
    cout<<t<<endl;
}

猜你喜欢

转载自blog.csdn.net/weixin_43353639/article/details/107992476
今日推荐