在字符串的比较中,除了暴力匹配,还可以利用转化的思想,将一串字符串映射为唯一整数,常见的是利用它们的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;
}