zoukankan      html  css  js  c++  java
  • 哈希

    哈希

    多哈希板子
    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

  • 相关阅读:
    Android中Tomcat的简单配置和使用
    Android Toast 总结(五种用法)
    软件工程—软件开发生命周期
    Android四大组件之BroadcastReceiver
    Android之 Fragment
    Android 四大组件之Acticity
    java 注解Annotation
    Android Intent的使用
    Android 调用webService(.net平台)
    关于TouchEvent中出现异常:MessageQueue-JNI问题
  • 原文地址:https://www.cnblogs.com/zx0710/p/14044767.html
Copyright © 2011-2022 走看看