字符串hash

目的:把字符串有效地转化为一个整数,极小的概率会出现两个字符串hash值相等。

学习链接:https://www.luogu.org/blog/pks-LOVING/zi-fu-chuan-xue-xi-bi-ji-ha-xi-hash-yu-zi-dian-shu-trie#

hash[i]=(hash[i-1]*p+idx(s[i]))%mod

hash[l..r]=(hash[r]-hash[l-1]*(p^(r-l+1))+mod)%mod

自动取模hash:unsigned long long hash[maxn],范围[0, 2^64) ,即mod=2^64

保险double hash,mod1=1e9+7,mod2=1e9+9,mod1与mod2为孪生素数,冲突概率极低(hash的维度越高,耗时越高,耗内存越大)

hash1[i]=(hash1[i-1]*p+idx(s[i]))%mod1

hash2[i]=(hash2[i-1]*p+idx(s[i]))%mod2

基本操作:二分+hash

 

自用模板:

P3370 【模板】字符串哈希:https://www.luogu.org/problemnew/show/P3370

#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
const int maxn=1e4+10;
ull base=131,mod=21237044013013795711;
int prime=233317;
ull hashe(char *s)
{
    int len=strlen(s);
    ull ans=0;
    for(int i=0;i<len;i++)
    {
        ans=(ans*base+(ull)s[i])%mod+prime;
    }
    return ans;
}
int main()
{
    int n,ans=1;
    char s[maxn];
    ull a[maxn]={0};
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s);
        a[i]=hashe(s);
    }
    sort(a+1,a+n+1);
    for(int i=1;i<n;i++)
    {
        if(a[i]!=a[i+1])ans++;
    }
    printf("%d\n",ans);
    return 0;
}
View Code

P2957谷仓里的回声Barn Echoes:https://www.luogu.org/problemnew/show/P2957,前缀后缀最长公共部分,kmp可做

#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int maxn=100;
ull base=131,mod=21237044013013795711;
int prime=233317;
ull hashe(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;
}

string s,s2;
ull a[maxn],a2[maxn];
int main()
{
    int ans=0,len;
    cin>>s>>s2;
    len=min(s.size(),s2.size());
    
    for(int i=1;i<=len;i++)
    {
        string tmp1=s.substr(s.size()-i,i),tmp2=s2.substr(0,i);
        if(hashe(tmp1)==hashe(tmp2)&&i>ans)ans=i;
    }
    for(int i=1;i<=len;i++)
    {
        string tmp1=s2.substr(s2.size()-i,i),tmp2=s.substr(0,i);
        if(hashe(tmp1)==hashe(tmp2)&&i>ans)ans=i;
    }
    
    printf("%d\n",ans);
    return 0;
} 
View Code

P1381单词背诵:https://www.luogu.org/problem/P1381,n个单词要背,m个单词序列,选一段序列使得包含要背诵的单词num越多越好,num相等时序列长度len越短越好,输出num和len。单词用hashe处理,方法为O(n)复杂度的尺取,刚开始wa在越多越好这个点,其实这个数num是可算的,m个单词里滚一边出现过几种要背的单词就好了。可能没出现过,这种情况放到while里不能跳出循环,因为sum和num始终为0,所以需要特判。

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ull unsigned long long
using namespace std;
const int maxn=1e5+10;
ull base=131,mod=21237044013013795711;
int prime=233317;
ull hashe(char *s)
{
    int len=strlen(s);
    ull ans=0;
    for(int i=0;i<len;i++)
    {
        ans=(ans*base+(ull)s[i])%mod+prime;
    }
    return ans;
}
map<ull,int> appear,mp,vis;
ull cnt[maxn];
int main()
{
    int n,m,num=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        char s[20];
        scanf("%s",s);
        ull tmp=hashe(s);
        appear[tmp]=1;
    }
    scanf("%d",&m);
    for(int i=0;i<m;i++)
    {
        char s[20];
        scanf("%s",s);
        ull tmp=hashe(s);
        cnt[i]=hashe(s);
        if(appear[tmp]==1&&!vis[tmp])num++,vis[tmp]=1;
    }
    if(num==0){printf("0\n0\n");return 0;}
    int st=0,en=0,sum=0,len=INF;
    while(1)
    {
        while(en<m&&sum<num)
        {
            if(mp[cnt[en]]==0&&appear[cnt[en]]==1)sum++;
            mp[cnt[en]]++;
            en++;
        }
        if(sum==num)len=min(len,en-st);
        if(sum<num)break;
        mp[cnt[st]]--;
        if(mp[cnt[st]]==0&&appear[cnt[st]]==1)sum--;
        st++;
    }
    printf("%d\n%d\n",num,len);
    return 0;
}
View Code

 

 

 

...

posted @ 2019-09-18 23:28  myrtle  阅读(189)  评论(0编辑  收藏  举报