文章目录
- 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值。
在这种转换,虽然字符串和整数一一对应,但是当遇到字符串长度较长时,产生过的整数会非常大,没办法用一般的数据类型保存。为了应对这种情况,我们舍弃一些“唯一性”,将产生的结果对一个整数除模。
实践发现,在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取模的操作。于是散列函数如下所示:
首先直接从进制转换考虑,H[i…j]实际上等于把str[i…j]从p进制转换为十进制的结果,于是:
然后通过H[j]的散列函数来推导结果:
因此有下面式子成立:
就得到了str[i…j]的hash值H[i…j],加上原先的取模操作就得到,
由于括号内部可能小于0,为使结果非负,首先对结果取模,然后再加上mod后再次取模,以得到正确的结果。
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;
}