0
点赞
收藏
分享

微信扫一扫

字符串hash——附带完整代码

余寿 2022-05-26 阅读 50


文章目录

  • ​​1 概念​​
  • ​​1.1 求字符串hash​​
  • ​​1.2 求字符串hash示例​​
  • ​​1.2.1 问题描述​​
  • ​​1.2.2 实现代码​​
  • ​​1.3 求字符串的子串的hash​​
  • ​​1.4 求字符串的子串hash 示例​​
  • ​​1.4.1 问题描述:​​
  • ​​1.4.2 实现代码:​​
  • ​​1.5 扩展​​

1 概念

1.1 求字符串hash

字符串hash: 一个字符串S映射一个为整数,使得该整数可以尽可能唯一地代表字符产S。

如把大写字母A~Z转换为0~25,H[i]表示字符串的前i个字符的hash,这样当i取遍0~len-1后,得到H[len-1]就是整个字符串的hash值。
字符串hash——附带完整代码_子串

在这种转换,虽然字符串和整数一一对应,但是当遇到字符串长度较长时,产生过的整数会非常大,没办法用一般的数据类型保存。为了应对这种情况,我们舍弃一些“唯一性”,将产生的结果对一个整数除模。
字符串hash——附带完整代码_#include_02
实践发现,在int范围内,将进制数设置为一个107级别的素数P(如10000019),同时把mod设置为一个109级别的素数(如1000000007),那么冲突产生的概率会变得非常小,很难产生冲突。

1.2 求字符串hash示例

1.2.1 问题描述

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

1.2.2 实现代码

#include 
#include
#include
#include
#include

using std::cin;
using std::string;
using std::sort;
using std::vector;

const int p = 10000019;
const int MOD = 1000000007;
vector<int> ans;

long long hashStr(string str){
long long res = 0;
for (int i = 0; i < str.length(); ++i)
{
res = (res * p + str[i] - 'a') % MOD;
}
return res;
}

int main(int argc, char const *argv[])
{
string str;
while(getline(cin, str), str != "#"){
long long id = hashStr(str);
ans.push_back(id);
}

sort(ans.begin(), ans.end());

int num = 0;
for (int i = 0; i < ans.size(); ++i)
{
if(i == 0 || ans[i] != ans[i-1]){
num++;
}
}

printf("%d\n", num);
return 0;
}

1.3 求字符串的子串的hash

为了方便讨论,暂时去掉求解H[i]时对mod取模的操作。于是散列函数如下所示:
字符串hash——附带完整代码_子串_03
首先直接从进制转换考虑,H[i…j]实际上等于把str[i…j]从p进制转换为十进制的结果,于是:
字符串hash——附带完整代码_子串_04
然后通过H[j]的散列函数来推导结果:

字符串hash——附带完整代码_子串_05
因此有下面式子成立:
字符串hash——附带完整代码_字符串_06
就得到了str[i…j]的hash值H[i…j],加上原先的取模操作就得到,
字符串hash——附带完整代码_#include_07
由于括号内部可能小于0,为使结果非负,首先对结果取模,然后再加上mod后再次取模,以得到正确的结果。
字符串hash——附带完整代码_子串_08

1.4 求字符串的子串hash 示例

1.4.1 问题描述:

输入两个长度均不超过1000的字符串,求它们的最长子串长度

1.4.2 实现代码:

//求最大公共子串长度
#include
#include
#include
#include
#include

using std::max;
using std::string;
using std::vector;
using std::cin;
using std::pair;
using std::make_pair;

typedef long long LL;

const int P = 10000019;
const int MOD = 1000000007;
const int MAXN = 1010;
LL PowP[MAXN];//powP存放P^i%MOD
LL H1[MAXN], H2[MAXN];//str1、str2的hash值
vector<pair<int, int>> pr1, pr2;//<子串hash值,子串长度>

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

//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;
}
}

//计算H[i...j]
int calSingleSubH(LL H[], int i, int j){
if(i == 0) return H[j];
else return ((H[j] - H[i-1] * PowP[j - i + 1]) % MOD + MOD)% MOD;
}

//计算所有子串的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(int argc, char const *argv[])
{
string str1, str2;
getline(cin, str1);
getline(cin, str2);

Init(max(str1.length(), str2.length()));
calH(H1, str1);
calH(H2, str2);

calSubH(H1, str1.length(), pr1);
calSubH(H2, str2.length(), pr2);

printf("ans = %d\n", getMax());
return 0;
}

1.5 扩展

如果在问题1.4的基础上,需要输出最大公共子串本身。

实现代码:

#include 
#include
#include
#include

using std::max;
using std::string;
using std::vector;
using std::cin;


typedef long long LL;
struct Node{
int hashValue, length, start, end;//字符串hash值、长度、起始为止、终止位置
Node(int _hashValue, int _length, int _start, int _end): hashValue(_hashValue), length(_length), start(_start), end(_end){}
};

const int P = 10000019;
const int MOD = 1000000007;
const int MAXN = 1010;
LL PowP[MAXN];//powP存放P^i%MOD
LL H1[MAXN], H2[MAXN];//str1、str2的hash值
vector<Node> pr1, pr2;//<子串hash值,子串长度>

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

//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;
}
}

//计算H[i...j]
int calSingleSubH(LL H[], int i, int j){
if(i == 0) return H[j];
else return ((H[j] - H[i-1] * PowP[j - i + 1]) % MOD + MOD)% MOD;
}

//计算所有子串的hash值,并将<子串hash,子串长度>存入pr
void calSubH(LL H[], int len, vector<Node>& pr){
for (int i = 0; i < len; ++i)
{
for (int j = i; j < len; ++j)
{
int hashValue = calSingleSubH(H, i, j);
pr.push_back(Node(hashValue, j - i + 1, i , j));
}
}
}


//计算pr1和pr2中相同hash值,维护最大长度
string getMax(string str1){
int ans = 0;
string str;
for (int i = 0; i < pr1.size(); ++i)
{
for (int j = 0; j < pr2.size(); ++j)
{
if(pr1[i].hashValue == pr2[j].hashValue){
if(pr1[i].length > ans){
ans = pr1[i].length;
str = str1.substr(pr1[i].start, pr1[i].end);
}
}
}
}
return str;
}

int main(int argc, char const *argv[])
{
string str1, str2;
getline(cin, str1);
getline(cin, str2);

Init(max(str1.length(), str2.length()));
calH(H1, str1);
calH(H2, str2);

calSubH(H1, str1.length(), pr1);
calSubH(H2, str2.length(), pr2);

string res = getMax(str1);
if(res.size() == 0){
printf("false\n");
}else{
std::cout << res << std::endl;
}

return 0;
}

举报

相关推荐

0 条评论