Multiple Hashing class

Revision en1, by Luisito_0_1_0_1, 2025-11-17 18:19:30

Hello again!

Previously, I published a blog about a class I came up with to help people learn Hashing.
Now, I come with another type of object: Hashing with 2 modules, and 2 non-module primes.
The probability of a collision in a Hashing can be calculated as:

$$$P_{\text{collision}} \approx \frac{1}{\prod_{i=1}^{k} \text{mod}_i}$$$


And with 1e6 comparisons between two substrings within the same string (usually, a problem with hashing requires this number of comparisons), with a single module it can result in collisions, and thus in solutions that give Wrong Answer.

However, the probabilities of collision in Hashing with more modules are approximately:

For 10⁶ comparisons: ≈ 2.5 × 10⁻²⁵
For 10⁹ comparisons: ≈ 2.5 × 10⁻¹⁶
For 10¹² comparisons: ≈ 2.5 × 10⁻⁷

So if in a problem you get Wrong Answer due to a collision, go win the lottery! (seriously)

Here is the class:

class HashStr{
	public:
    int md;
    int prim;
    string str;
    vector<int>pot;
    vector<int>hash;
    HashStr(){};
    HashStr(string s,const int& _md,const int& _prim){
        md=_md;
        prim=_prim;
        str=s;
        pot.push_back(1);
        hash.push_back(0);
        for(int i=0;i<s.size();i++){
            pot.push_back((pot[i]*prim)%md);
            hash.push_back((hash[i]*prim+(s[i]))%md);
        }
    }
    HashStr(const HashStr& x){
        md=x.md;
        prim=x.prim;
        str=x.str;
        pot=x.pot;
        hash=x.hash;
    }
    int gethash(int l,int r){
        int re=(hash[r]-hash[l-1]*pot[r-l+1])%md;
        return re<0?re+md:re;
    }
    void operator=(const HashStr& x){
        md=x.md;
        prim=x.prim;
        str=x.str;
        hash=x.hash;
        pot=x.pot;
    }
};
class Value{
    public:
    int h1,h2,h3,h4;
    Value(){}
	Value(const int& a,const int& b,const int& c,const int& d):h1(a),h2(b),h3(c),h4(d){}
	bool operator==(const Value& v)const{
		return h1==v.h1&&h2==v.h2&&h3==v.h3&&h4==v.h4;
	}
	bool operator<(const Value& v)const{
	    if(h1!=v.h1)return h1<v.h1;
        if(h2!=v.h2)return h2<v.h2;
        if(h3!=v.h3)return h3<v.h3;
        return h4<v.h4;
	}
	void operator=(const Value& v){
        h1=v.h1;
        h2=v.h2;
        h3=v.h3;
        h4=v.h4;
	}
};
class HashedStr{
	public:
	HashStr h1,h2,h3,h4;
    HashedStr(){}
    HashedStr(string s):h1(s,1e9+7,131),h2(s,1e9+9,131),h3(s,1e9+7,137),h4(s,1e9+9, 137){}
	Value gethash(int l,int r){
		return Value(h1.gethash(l,r),h2.gethash(l,r),h3.gethash(l,r),h4.gethash(l,r));
	};
};
class HashMap{
    public:
    map<Value,int>m;
    HashMap(){}
    void push(const Value& v){m[v]++;}
    void push(const string& s){
        HashedStr v(s);
        m[v.gethash(1,s.size())]++;
    }
    void push_all_sub(const string& s,const int&n){
        HashedStr h(s);
        for(int i=n;i<=s.size();i++){
            m[h.gethash(i-n+1,i)]++;
        }
    }
    void push_all_sub(const string& s){
        HashedStr h(s);
        for(int i=1;i<=s.size();i++){
            for(int j=i;j<=s.size();j++){
                m[h.gethash(i,j)]++;
            }
        }
    }
    void push_all_pref(const string& s){
        HashedStr h(s);
        for(int i=1;i<=s.size();i++){
            m[h.gethash(1,i)]++;
        }
    }
    void push_all_suf(const string& s){
        HashedStr h(s);
        for(int i=1;i<=s.size();i++){
            m[h.gethash(s.size()-i+1,s.size())]++;
        }
    }
    void take(const Value& v){m[v]--;}
    void take(const string& s){
        HashedStr v(s);
        m[v.gethash(1,s.size())]--;
    }
    int count(const Value& v){return m[v];}
    int count(const string& s){
        HashedStr v(s);
        return m[v.gethash(1,s.size())];
    }
};


Usage examples:

int32_t main(){
    string s="abcabcabababc";

    HashStr h1(s,1e9+7,31); //1 hash
    HashedStr h2(s); // 4 hashes

    cout<<"HashStrOutputs:"<<endl<<endl;
    
    cout<<h1.gethash(1,2)<<endl; //hash of substring "ab"
    cout<<h1.gethash(1,5)<<endl; //hash of substring "abcab"
    cout<<h1.gethash(4,8)<<endl; //hash of substring "abcab"

    cout<<endl<<"HashedStrOutputs:"<<endl<<endl;

    cout<<(h2.gethash(1,5)==h2.gethash(4,8))<<endl; //comparing "abcab" with "abcab"
                                                    //       abcabcabababc
                                                    //       ^   ^
                                                    //       abcab
                                                    //          ^   ^
                                                    //          abcab

    cout<<(h2.gethash(7,9)==h2.gethash(9,11))<<endl; //comparing "aba" with "aba"

    cout<<(h2.gethash(1,5)==h2.gethash(7,11))<<endl<<endl; //comparing "abcab" with "ababc"

    cout<<"HashMapOutputs:"<<endl<<endl;

    HashMap m;

    m.push("ab");
    m.push("abab");
    m.push("ab");
    m.push("ab");

    cout<<m.count("ab")<<endl<<endl; //"ab" pushed 3 times

    HashMap m2;

    m2.push_all_sub("abcabc"); //This push: "a","b","c","a","b","c","ab","bc","ca","ab","bc","abc"
                              //"bca","cab","abc","abca","bacb","cabc","abcab","bcabc","abcabc"
    
    cout<<m2.count("ab")<<endl<<endl;
    
    HashedStr h("abcabc");

    Value a=h.gethash(1,1); //Multiple-mod-hash-value of "a"
    Value b=h.gethash(1,2); //Multiple-mod-hash-value of "ab"
    Value c=h.gethash(3,6); //Multiple-mod-hash-value of "cabc"

    cout<<m2.count(a)<<endl; //"a" pushed 2 times
    cout<<m2.count(b)<<endl; //"ab" pushed 2 times
    cout<<m2.count(c)<<endl<<endl; //"cabc" pushed 1 times

    HashMap m3;
    m3.push_all_pref("abcdefg"); //This push: "a","ab","abc","abcd","abcde","abcdef","abcdefg"
    m3.push_all_suf("abcdefg"); //This push: "g","fg","efg","defg","cdefg","bcdefg","abcdefg"

    cout<<m3.count("a")<<endl; //"a" pushed 1 time
    cout<<m3.count("abcdefg")<<endl; //abcdefg pushed 2 times

    m3.take("a");

    cout<<m3.count("a")<<endl; //"a" doesnt exist
}


I hope this can be very helpful, thank you for reading me :D

History

 
 
 
 
Revisions
 
 
  Rev. Lang. By When Δ Comment
en1 English Luisito_0_1_0_1 2025-11-17 18:19:30 6323 Initial revision (published)