Loading

<数据结构>hash进阶

hash函数构建

  • 采取26进制

    对于字符串str,令**H[i] = H[i-1]*26 + index(str[i]) **,最后H[i-1]就是str的hash值

    问题:hash值过大,无法表式

  • 取模

    在上述基础上取模:H[i] = (H[i-1]*26 + index(str[i]))%mod

    问题:丧失了一定的唯一性

  • 权衡:一个冲突概率极小的hash函数

    H[i] = (H[i-1]*p + index(str[i]))%mod

    其中p=107 数量级的素数(10000019),mod=109 数量级的素数(1000000007)

例1:判断不同的字符串个数

问题描述

给出N个只有小写字母的字符串,判断其中不同的字符串的个数

代码实现

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
const int MOD = 1000000007;
const int P = 10000019;
vector<int> ans;
//字符串hash
long long hashFunc(string str){
    long long H = 0;
    for(int i = 0; i < str.length(); i++){
        H = (H*P + str[i] - 'a') % MOD;
    }
    return H;
}
int main(){
    string str;
    while(getline(cin, str), str != "#"){
        long long id = hashFunc(str);
        ans.push_back(id);
    } 
    sort(ans.begin(), ans.end());
    int count = 0;
    for(int i = 0; i < ans.size(); i++){
        if(i == 0 || ans[i] != ans[i-1])
            count++;
    }
    cout<< count << endl;
    return 0;
}

例2: 最长公共子串

前置:求解子串str[i…j]的hash值H[i…j]

符号含义:

  • H[i..j] : str[i]~str[j]这一子串对应的hash值,即该子串对应的p进制数
  • H[i] : H[0…i]

H[i…j] = index(str[i]) * pj-i + index(str[i-1]) * pj-i-1 + … + index(str[j]) * p0

H[i] = H[i-1] * p + index(str[i])

于是 :

QQ截图20211204110019

所以:

H[i…j] = H[j] - H[i - 1] * pj-i+1求完str的H数组后,直接调取下标j 和 i-1 即可求得

取模:

H[i…j] = (H[j] - H[i - 1] * pj-i+1)%mod

非负处理:(括号内可能为负值)加模再取模

H[i…j] = ((H[j] - H[i - 1] * pj-i+1)%mod + mod)%mod

步骤

  1. 计算H[]数组
  2. 求出两个字符串所有子串的hash值以及对应的长度
  3. 子串两两比较,得出长度最大值

代码

#include<iostream>
#include<cstdio>
#include<string>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL MOD = 1000000007;
const LL P = 10000019;
const int MAXN = 1010;  //MAXN为字符串的最大长度
//powP[i]存放p^i%MOD, H1,H2分别存放str1,str2的hash值
LL powP[MAXN], H1[MAXN] = {0}, H2[MAXN] = {0};
//pr1存放所有<子串hash值,子串长度>, pr2同理
vector<pair<int,int>> pr1,pr2;

//init函数初始化powP
void init(int len){
    powP[0] = 1;
    for(int i = 1; i <= len; i++){
        powP[i] = (powP[i-1]*P)%MOD;
    }
} 

//calH函数计算字符串str的hash值
void calH(LL H[], string &str){
    H[0] = str[0];
    for(int i = 1; i < str.length(); i++){
        H[i] = (H[i-1]*P + str[i])%MOD;
    }
}
//calSingleSubH 计算 H[i...j]
int calSingleSubH(LL H[], int i, int j){
    if(i == 0) return H[j];
    return ((H[j] - H[i-1] * powP[j - i + 1])%MOD + MOD)%MOD;
}
//calSubH 计算 所有子串的hash值,并将<子串hash值,子串长度>存入pr
void calSubH(LL H[], int len, vector<pair<int,int>> &pr){
    for(int i = 0; i < len; i++){
        for(int j = i; j < len; j++){
            int hashValue = calSingleSubH(H, i, j);
            pr.push_back(make_pair(hashValue, j - i + 1));
        }
    }
}
//计算 pr1 和 pr2中相同的 hash值, 维护最大长度
int getMax(){
    int ans = 0;
    for(int i = 0; i < pr1.size(); i++){
        for(int j = 0; j < pr2.size(); j++){
            if(pr1[i].first == pr2[j].first)
                ans = max(ans, pr1[i].second);
        }
    }
    return ans;
}

int main(){
    string str1, str2;
    getline(cin, str1);
    getline(cin, str2);
    init(max(str1.length(), str2.length())); //初始化powP数组
    calH(H1,str1);  //分别计算 str1 和 str2 的hash值
    calH(H2,str2);
    calSubH(H1,str1.length(),pr1);  //分别计算所有H1[i...j] 和 H2[i...j]
    calSubH(H2,str2.length(),pr2);
    printf("ans = %d", getMax());  //输出最大公共子串长度
    return 0;
}
posted @ 2021-12-04 12:27  咪啪魔女  阅读(46)  评论(0编辑  收藏  举报