哈希

哈希

多哈希板子
struct Hash{
    //胡老板的板子很干净,但多哈希相对慢一些,大约两倍时长
    //从下标为1开始用
    int base[4],mod[4];
    int tot,hash[4][maxn],pw[4][maxn]; //字符串长度,hash:记从1~i的hash值,pw:记录base^i
    Hash() {
        tot=0;
        for(int i=1;i<=3;i++) pw[i][0]=1;
        base[1]=233;base[2]=19260817;base[3]=20030714;//其余模数 39
        mod[1]=1e9+7;mod[2]=1e9+9;mod[3]=998244353;
    }
    void init() {tot=0;}
    void insert(int c) {
        tot++;
        for(int i=1;i<=3;i++) hash[i][tot]=(1LL*hash[i][tot-1]*base[i]+c)%mod[i];
        for(int i=1;i<=3;i++) pw[i][tot]=(1LL*pw[i][tot-1]*base[i])%mod[i];
    }
    //字符串[l,r]hash值,type为第几个hash
    int query(int l,int r,int type) { 
        return (hash[type][r]-(1LL*hash[type][l-1]*pw[type][r-l+1]%mod[type])+mod[type])%mod[type];
    }
    //判断字符串u的[lu,ru]内的字符串和字符串v的[lv,rv]内的字符串是否相同
    friend bool same(Hash &u,int lu,int ru,Hash &v,int lv,int rv) { 
        if(ru-lu!=rv-lv) return false;
        for(int i=1;i<=3;i++) if(u.query(lu,ru,i)!=v.query(lv,rv,i)) return false;
        return true;
    }
}h1,h2;
相似字符串:E - Check Transcription

题意:给你一个01串s(\(2≤|s|≤10^5\)) ,给你一个普通字符串t(\(1≤|t|≤10^6\)),请你找有多少种方案令"0"=s0,"1"=s1,使得s映射后=t,要求s0不等于s1,且两者非空。题目保证s串中至少有一个0和1。

题解:没有题解,直接哈希

注意点:

1.复杂度计算,次数大约是\(\sum_{i=1}^t\frac{t}{i}\approx tln(t)\),所以才可以这样哈希过,推导过程:

image-20201126171610430注:倒数第二行的角标写错了i--1

image-20201126164146640当你在cf这种地方想要单哈希过题,而且想用自然溢出;

image-20201126164522337当你几个小时终于妥协决定加个模数

3.strlen(s+1)//读入的时候有+1,这里不能忘记+1,不然debug会像我一样痛苦

#include<bits/stdc++.h>
using namespace std;
#define debug(x) cout<<#x<<':'<<x<<endl;
typedef unsigned long long ull;
const int maxn=2e6+100;
const ull base=39;
const ull mod=1e9+7;
char s[maxn],t[maxn];
ull hash_t[maxn],p[maxn];
int lens,lent,num0,num1;
bool solve(int s0)//表示0,1对应的字符串长度
{
    int s1=(1ll*lent-(1ll*s0*num0))/num1;
    ull H0=0,H1=0,B0=p[s0],B1=p[s1];
    int set0=0,set1=0;
    for(int i=1,j=1;i<=lens;i++){
        if(s[i]=='0')
        {
            if(set0==0)
            {
                set0=1;
                H0=(hash_t[j+s0-1]-hash_t[j-1]*B0%mod+mod)%mod,j+=s0;//debug(H0);
                if(H1==H0&&set1==1) {return false;}
                else continue;
            }
            ull temp_hash0=(hash_t[j+s0-1]-hash_t[j-1]*B0%mod+mod)%mod;
            if(temp_hash0==H0)j+=s0;
            else { return false;}
        }
        else if(s[i]=='1')
        {
            if(set1==0)
            {
                set1=1;
                H1=(hash_t[j+s1-1]-hash_t[j-1]*B1%mod+mod)%mod,j+=s1;//debug(H1);
                if(H1==H0&&set0==1){return false;}
                else {continue;}
            }
            ull temp_hash1=(hash_t[j+s1-1]-hash_t[j-1]*B1%mod+mod)%mod;
            if(temp_hash1==H1)j+=s1;
            else return false;
        }
    }
    return true;
}
int main()
{
    scanf("%s%s",s+1,t+1);
    lens=strlen(s+1),lent=strlen(t+1),num0=0,num1=0;
    ull ans=0;
    for(int i=1;i<=lens;i++){
        if(s[i]=='0') num0++;
        else num1++;
    }
    p[0]=1;
    for(int i=1;i<=lent;i++) p[i]=p[i-1]*base%mod;
    for(int i=1;i<=lent;i++) hash_t[i]=(hash_t[i-1]*base+t[i])%mod;
    for(int i=1;1ll*i*num0+num1<=lent;i++)//枚举0对应字符串的长度
    {
        if((1ll*lent-(1ll*i*num0))%num1!=0) continue;
        if(solve(i)) ans++;
    }
    printf("%llu",ans);
}

用板子写的多哈希版本:

#include<bits/stdc++.h>
using namespace std;
#define debug(x) cout<<#x<<':'<<x<<endl;
const int maxn=1e6+100;
struct Hash{
    int base[4],mod[4];
    int tot,hash[4][maxn],pw[4][maxn]; //字符串长度,hash:记从1~i的hash值,pw:记录base^i
    Hash() {
        tot=0;
        for(int i=1;i<=3;i++) pw[i][0]=1;
        base[1]=233;base[2]=19260817;base[3]=20030714;
        mod[1]=1e9+7;mod[2]=1e9+9;mod[3]=998244353;
    }
    void init() {
        tot=0;
    }
    void insert(int c) {
        tot++;
        for(int i=1;i<=3;i++) hash[i][tot]=(1LL*hash[i][tot-1]*base[i]+c)%mod[i];
        for(int i=1;i<=3;i++) pw[i][tot]=(1LL*pw[i][tot-1]*base[i])%mod[i];
    }
    //字符串[l,r]hash值,type为第几个hash
    int query(int l,int r,int type) {
        return (hash[type][r]-(1LL*hash[type][l-1]*pw[type][r-l+1]%mod[type])+mod[type])%mod[type];
    }
    //判断字符串u的[lu,ru]内的字符串和字符串v的[lv,rv]内的字符串是否相同
    friend bool same(Hash &u,int lu,int ru,Hash &v,int lv,int rv) {
        if(ru-lu!=rv-lv) return false;
        for(int i=1;i<=3;i++) if(u.query(lu,ru,i)!=v.query(lv,rv,i)) return false;
        return true;
    }
}h;
char s[maxn],t[maxn];
int main()
{
    h.init();
    scanf("%s%s",s+1,t+1);int ans=0;
    int num0=0,num1=0,lens=strlen(s+1),lent=strlen(t+1);
    for(int i=1;i<=lent;i++) h.insert(t[i]);
    if(s[1]=='1') for(int i=1;i<=lens;i++) s[i]=(s[i]=='0'?'1':'0');//'0'和‘1‘互换
    for(int i=1;i<=lens;i++)
    {
        if(s[i]=='0')num0++;
        else num1++;
    }
    for(int s0=1;1ll*s0*num0+num1<=lent;s0++)
    {
        bool flag=true;int start1=-1;//1对应的一个序列开始的位置;
        if((lent-1ll*s0*num0)%num1!=0) continue;
        int s1=(lent-1ll*s0*num0)/num1;
        for(int i=1,j=1;i<=lens;i++)
        {
            if(s[i]=='0'){
                if(!same(h,1,s0,h,j,j+s0-1)){flag=false;break;}
                j+=s0;
            }
            else {
                if(start1==-1) {start1=j;j+=s1; continue;}
                else if(!same(h,start1,start1+s1-1,h,j,j+s1-1)){flag=false;break;}
                j+=s1;
            }
        }
        if(flag&&!same(h,start1,start1+s1-1,h,1,s0)) {ans++;}
    }
    printf("%d",ans);
}
压缩字符串: 1200E - Compress Words

题意:image-20201126193954040经典看样例猜题意

tag:哈希,kmp裸题

#include<bits/stdc++.h>
using namespace std;
#define debug(x) cout<<#x<<':'<<x<<endl;
const int maxn=1e6+100;
struct Hash{
    int base[4],mod[4];
    int tot,hash[4][maxn],pw[4][maxn]; //字符串长度,hash:记从1~i的hash值,pw:记录base^i
    Hash() {
        tot=0;
        for(int i=1;i<=3;i++) pw[i][0]=1;
        base[1]=233;base[2]=19260817;base[3]=20030714;//其余模数 39
        mod[1]=1e9+7;mod[2]=1e9+9;mod[3]=998244353;
    }
    void init() {tot=0;}
    void insert(int c) {
        tot++;
        for(int i=1;i<=3;i++) hash[i][tot]=(1LL*hash[i][tot-1]*base[i]+c)%mod[i];
        for(int i=1;i<=3;i++) pw[i][tot]=(1LL*pw[i][tot-1]*base[i])%mod[i];
    }
    //字符串[l,r]hash值,type为第几个hash
    int query(int l,int r,int type) {
        return (hash[type][r]-(1LL*hash[type][l-1]*pw[type][r-l+1]%mod[type])+mod[type])%mod[type];
    }
    //判断字符串u的[lu,ru]内的字符串和字符串v的[lv,rv]内的字符串是否相同
    friend bool same(Hash &u,int lu,int ru,Hash &v,int lv,int rv) {
        if(ru-lu!=rv-lv) return false;
        for(int i=1;i<=3;i++) if(u.query(lu,ru,i)!=v.query(lv,rv,i)) return false;
        return true;
    }
}ansh,h;
int main()
{
    int n;cin>>n;string ans,s;
    cin>>ans;ansh.init();
    for(int i=0;i<ans.length();i++)ansh.insert(ans[i]);//稍微注意一下下标
    for(int i=2;i<=n;i++)
    {
        cin>>s;int jmax=-1;h.init();//jmax设为-1,是为了考虑完全没匹配上的情况
       for(int j=0;j<min(s.length(),ans.length());j++)
       {
           h.insert(s[j]);
           if(same(ansh,(int)ans.size()-j,(int)ans.size(),h,1,j+1)) jmax=j;
       }
       jmax++;
       ans+=s.substr(jmax,s.length()-jmax);
       for(;jmax<s.length();jmax++)
           ansh.insert(s[jmax]);
    }
    cout<<ans<<endl;
}
找包含三个串的串最短长度:E - Test

题意:给定三个字符串,求一个长度最短的字符串使这三个串是它的子串。

题解:跟上题相仿,但要先暴力判断是不是已经是子串了,然后枚举6种排列组合

#include<bits/stdc++.h>
using namespace std;
#define debug(x) cout<<#x<<':'<<x<<endl;
const int maxn=1e6+100;
struct Hash{
    int base[4],mod[4];
    int tot,hash[4][maxn],pw[4][maxn]; //字符串长度,hash:记从1~i的hash值,pw:记录base^i
    Hash() {
        tot=0;
        for(int i=1;i<=3;i++) pw[i][0]=1;
        base[1]=233;base[2]=19260817;base[3]=20030714;//其余模数 39
        mod[1]=1e9+7;mod[2]=1e9+9;mod[3]=998244353;
    }
    void init() {tot=0;}
    void insert(int c) {
        tot++;
        for(int i=1;i<=3;i++) hash[i][tot]=(1LL*hash[i][tot-1]*base[i]+c)%mod[i];
        for(int i=1;i<=3;i++) pw[i][tot]=(1LL*pw[i][tot-1]*base[i])%mod[i];
}
//字符串[l,r]hash值,type为第几个hash
int query(int l,int r,int type) {
    return (hash[type][r]-(1LL*hash[type][l-1]*pw[type][r-l+1]%mod[type])+mod[type])%mod[type];
}
//判断字符串u的[lu,ru]内的字符串和字符串v的[lv,rv]内的字符串是否相同
friend bool same(Hash &u,int lu,int ru,Hash &v,int lv,int rv) {
        if(ru-lu!=rv-lv) return false;
        for(int i=1;i<=3;i++) if(u.query(lu,ru,i)!=v.query(lv,rv,i)) return false;
        return true;
    }
}ansh,h;

int solve(string s1,string s2,string s3)
{
    string ans=s1;
    ansh.init();
    for(int i=0;i<ans.length();i++)ansh.insert(ans[i]);//稍微注意一下下标
    int jmax=-1;h.init();//jmax设为-1,是为了考虑完全没匹配上的情况
    for(int j=0;j<min(s2.length(),ans.length());j++)
    {
            h.insert(s2[j]);
            if(same(ansh,(int)ans.size()-j,(int)ans.size(),h,1,j+1)) jmax=j;
    }
        jmax++;
        ans+=s2.substr(jmax,s2.length()-jmax);
        for(;jmax<s2.length();jmax++)
            ansh.insert(s2[jmax]);

    jmax=-1;h.init();//jmax设为-1,是为了考虑完全没匹配上的情况
    for(int j=0;j<min(s3.length(),ans.length());j++)
    {
        h.insert(s3[j]);
        if(same(ansh,(int)ans.size()-j,(int)ans.size(),h,1,j+1)) jmax=j;
    }
    jmax++;
    ans+=s3.substr(jmax,s3.length()-jmax);
    for(;jmax<s3.length();jmax++)
        ansh.insert(s3[jmax]);
    return ans.length();
}

string s[3];
bool cmp(string a,string b){return a.length()<b.length();}
bool in(string a,string b)//判断a是不是in b
{
    for(int i=0;i<b.length()-a.length()+1;i++)
    {
        if(a==b.substr(i,a.length())) return true;
    }
    return false;
}
int main()
{
   int ans=0x3f3f3f3f;
   for(int i=0;i<3;i++)cin>>s[i];
   sort(s,s+3,cmp);
   if(in(s[1],s[2])) s[1]=s[2];
   if(in(s[0],s[1])) s[0]=s[1];
    if(in(s[0],s[2])) s[0]=s[2];
   ans=min(ans,solve(s[0],s[1],s[2])); ans=min(ans,solve(s[0],s[2],s[1]));
    ans=min(ans,solve(s[1],s[2],s[0])); ans=min(ans,solve(s[1],s[0],s[2]));
    ans=min(ans,solve(s[2],s[0],s[1]));ans=min(ans,solve(s[2],s[1],s[0]));
    cout<<ans<<endl;
}
找斐波那契数:HDU - 6768

题意:定义F[i]为斐波那契数列第i项,F[1]=1, F[2]=2, F[i]=F[i−1]+F[i−2] (i≥3)

已知任意正整数x都拥有一个唯一的长度为n的01数列b[n],使得

b[1]∗F[1]+b[2]∗F[2]+...+b[n]∗F[n]=x,bi∈{0,1}

以这样的表示法给出A、B、C三个数

已知数字C是由A∗B的结果在这样的表示法下将某个原本是1的位置改成0得来的

问抹去的是哪个位置

题解:哈希处理A,B,C,自然溢出,然后直接A*B-C,看对应哪个斐波那契数

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include<bits/stdc++.h>
using namespace std;
using namespace __gnu_pbds;
typedef unsigned long long ull;//自然溢出
gp_hash_table<ull,int>mp;
namespace IO
{
    #define gc getchar()

     template<typename T>inline void read(T &x) {
        x=0;
        char ch=gc;
        while(ch<='9'&&ch>='0')
        {
            x=(x<<3)+(x<<1)+ch-'0';
            ch=gc;
        }
        return;

    }
}
ull fibo[2000050];
int main()
{

    fibo[0]=fibo[1]=1;mp[1]=1;
    for(int i=2;i<2000050;i++)
    {
        fibo[i]=fibo[i-1]+fibo[i-2];
        mp[fibo[i]]=i;
    }
    int T;IO::read(T);
    while(T--)
    {
        int cntA,cntB,cntC,d;
        ull A=0,B=0,C=0;
        IO::read(cntA);for(int i=1;i<=cntA;i++){IO::read(d); A+=d*fibo[i];}
        IO::read(cntB);for(int i=1;i<=cntB;i++){IO::read(d); B+=d*fibo[i];}
        IO::read(cntC);for(int i=1;i<=cntC;i++){IO::read(d); C+=d*fibo[i];}
        printf("%d\n",mp[A*B-C]);
    }
    return 0;
}
二维哈希:POJ - 3690

题意:看样例猜题意,多组样例,第一行:N,M,T,P,Q

给一个NxM的矩阵,从中找有PxQ的那T个矩阵出现了多少个

Sample Input

3 3 2 2 2
*00
0**
*00

**
00

*0
**
3 3 2 2 2
*00
0**
*00

**
00

*0
0*
0 0 0 0 0

Sample Output

Case 1: 1
Case 2: 2
#include<iostream>
#include<set>
#include<stdio.h>
using namespace std;
typedef unsigned long long ull;
const int MAX_SIZE = 1005;
const int MAX_T = 105;

int N,M,T,P,Q,test=0;
char field[MAX_SIZE][MAX_SIZE];
char patterns[MAX_T][MAX_SIZE][MAX_SIZE];
ull h[MAX_SIZE][MAX_SIZE],tmp[MAX_SIZE][MAX_SIZE];
const ull B1 = 9973;
const ull B2 = 100000007;

void compute_hash(char a[MAX_SIZE][MAX_SIZE],int n,int m){
    //按行计算哈希值
    ull t1=1;
    for (int i = 0; i <Q ; ++i) t1*=B1;
    for(int i=0;i<n;i++)
    {
        ull e=0;
        for(int j=0;j<Q;j++)
            e=e*B1+a[i][j];
        tmp[i][0]=e;
        for(int j=1;j+Q<=m;j++)
            tmp[i][j]=tmp[i][j-1]*B1-a[i][j-1]*t1+a[i][j+Q-1];

    }
    ull t2=1;
    for(int i=0;i<P;i++) t2*=B2;
    for(int j=0;j<m-Q+1;j++)
    {
        ull e=0;
        for(int i=0;i<P;i++)
            e=e*B2+tmp[i][j];
        h[0][j]=e;
        for(int i=1;i+P<=n;i++)
            h[i][j]=h[i-1][j]*B2-tmp[i-1][j]*t2+tmp[i+P-1][j];
    }
}
void solve(){
    multiset<ull> unseen;
    for(int k=0;k<T;k++){
        compute_hash(patterns[k],P,Q);
        unseen.insert(h[0][0]);
    }
    compute_hash(field,N,M);
    for(int i=0;i+P<=N;i++){
        for(int j=0;j+Q<=M;j++){
            unseen.erase(h[i][j]);
        }
    }
    int ans = T - unseen.size();
    printf("Case %d: %d\n",++test,ans);
}
int main(){
    while(~scanf("%d %d %d %d %d", &N, &M, &T, &P, &Q)&&N&&M&&T&&P&&Q){
        for(int i=0;i<N;i++) scanf("%s",field[i]);
        for(int t=0;t<T;t++) for(int i=0;i<P;i++) scanf("%s",patterns[t][i]);
        solve();
    }
    return 0;
}

复习二维哈希的时候补

C - Where's Wally

posted @ 2020-11-26 21:43  zx0710  阅读(92)  评论(0编辑  收藏  举报