数据结构-字符串哈希

在字符串的比较中,除了暴力匹配,还可以利用转化的思想,将一串字符串映射为唯一整数,常见的是利用它们的ASCII码表进行一系列运算。

如果是有关顺序的绝对匹配,我们可以遍历字符串中每个字符,令不同位置的字符ASCII码乘以不同的基数次幂,再依次相加

如abc,如果基数为131,则哈希值='a'*131^2+'b'*131^1+'c'*131^0=2105375

而cba则是'c'*131^2+'b'*131^1+'a'*131^0=1711874

这样用不同的数字代表不同的字符串,就可以进行字符串的快速匹配

取哈希值的方法并不唯一,一般利用哈希值来进行匹配时,会设置辗转相乘的基数与模值来保证其哈希值尽量唯一(注意hash不可做函数名,与关键字冲突)

ull hash1(string s){
	ull res=0;
	for (int i=0;s[i];i++){
		res=(res*base1+(ull)s[i])%mod1;
	}
	return res;

例题

洛谷P3370 【模版】字符串哈希

为了令哈希值尽量唯一,基数和模需要尽量取质数,模尽量取八位质数。

另外,双哈希值可以使判断更为准确,即

typedef unsigned long long ull;
ull base1=131;
ull base2=103;
ull mod1=10657601;
ull mod2=10004243;
struct node{
	ull x,y;
}a[10010];
... ...(省略)
a[i].x=hash1(s);
a[i].y=hash2(s);

完整代码(双哈希法)

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
ull base1=131;
ull base2=103;
ull mod1=10657601;
ull mod2=10004243;//两对基数和模
struct node{
	ull x,y;
}a[10010];
bool cmp(node a,node b) {
    if (a.x!=b.x) {
        return a.x<b.x;
    } else {
        return a.y<b.y;
    }
}
ull hash1(string s){
	ull res=0;
	for (int i=0;s[i];i++){
		res=(res*base1+(ull)s[i])%mod1;
	}
	return res;
}
ull hash2(string s){
	ull res=0;
	for (int i=0;s[i];i++){
		res=(res*base2+(ull)s[i])%mod2;
	}
	return res;
}
int main(){
	int n;
	cin>>n;
	int i=0;
	int k=n;
	while (n--){
		string s;
		cin>>s;
		a[i].x=hash1(s);//计算哈希值
		a[i].y=hash2(s);
		i++;
	}
	sort(a,a+k,cmp);
	int c=k;
	for (i=0;i<k-1;i++){
		if (a[i].x==a[i+1].x&&a[i].y==a[i+1].y) c--;
	}
	cout<<c;
	return 0;
}

也可利用自然数溢出来保证值的唯一性(unsigned long long类型的数会对溢出2^64的值自动取溢出部分)

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
ull base=103;
ull a[10010];
ull hash1(string s){
	ull res=0;
	for (int i=0;s[i];i++){
		res=res*base+(ull)s[i];//利用自然数溢出,不必取模
	}
	return res;
}
int main(){
	int n;
	cin>>n;
	int i=0;
	int k=n;
	while (n--){
		string s;
		cin>>s;
		a[i]=hash1(s);
		i++;
	}
	sort(a,a+k);
	int c=k;
	for (i=0;i<k-1;i++){
		if (a[i]==a[i+1]) c--;
	}
	cout<<c;
	return 0;
}

对于不是绝对顺序的匹配,可以为每一个字母设置单独的取哈希值的规则,并且尽量保证各字符哈希值直接没有倍数关系,否则最终和会造成哈希值重复

洛谷P8630 [蓝桥杯2015国B] 密文搜索

题目要求在母串中找到与子串匹配的所有长度为8的字符串,并且顺序可打乱

那么就可以为每个字符单独设计哈希规则,即设计不同的基数

for (int i=1; i<=26; i++) {
		base[i]=i*('a'+i);
	}

完整代码如下

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
ull a[100000010];
ull base[27];
void hash1(string s) {//将母串中每一段长度为8的字符串哈希值保存记录
	int l=s.size()-8;
	for (int i=0; i<=l; i++) {
		int res=0;
		for (int j=i; j<i+8; j++) {
			res+=(ull)s[j]*base[s[j]-'a'+1];
		}
		a[i]=res;
	}
}
ull hash2(string s) {//计算子串哈希值
	int res=0;
	for (int i=0; i<8; i++) {
		res+=(ull)s[i]*base[s[i]-'a'+1];
	}
	return res;
}
int main() {
	for (int i=1; i<=26; i++) {//设置每种字符单独的哈希规则
		base[i]=i*('a'+i);
	}
	string s;
	cin>>s;
	ull l=s.size();
	hash1(s);
	int n;
	cin>>n;
	int c=0;
	while(n--) {
		string str;
		cin>>str;
		ull b=hash2(str);
		for (int i=0; i<=l-8; i++) {
			if (a[i]==b) c++;//记录匹配结果
		}
	}
	cout<<c;
	return 0;
}

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值