字符串哈希

一,定义

字符串哈希其实就是把一段字符串转化成一个数字。
在进行字符串匹配时不需要再 O (n)匹配字符串本身,而只需要匹配两个字符串的哈希值就好了O(1)

二,实现

h a s h ( s ) = Σ i = 0 n s [ i ] ∗ p n − i   % m o d hash(s)=\Sigma_{i=0}^{n}s[i]*p^{n-i}\ \%mod hash(s)=Σi=0ns[i]pni %mod
其中,n为字符串长度,s为该字符串,
p为基数,根据题目要求选择:ASCLL选择128,小写or大写字符可以采用26

哎!这时候就有聪明的同学发现了一个问题:

*采一朵花送给妈妈,妈妈,妈妈*
*作业写完了没!!!*

当我们算出哈希值并且mod完之后,想要写入时
这个位置已经被用过了

例如:
114%14=2
128%14=2

这就非常难受了
对此,我们有两种解决方法

一,开放寻址法
遇到冲突时,我们通过一定策略向后或者向前寻找空的位置并填入

二,链表法
如果遇到冲突,我们可以在当前节点后跟一个节点,形成一个链表,

但都太麻烦了,不想写,实际过程中我们可以尽量mod大质数来避免哈希冲突
例如:1e9+7,998244353…

下面来看代码:

计算哈希值

#define ll int
const ll mod=998244353;
ll hashp(string s){
	long long ans=0;
	for(int i=0;i<s.size();i++){
		ans=(ans*128%mod+1ll*s[i])%mod;
	}
	return ans%mod;
}

求字串的哈希值:
这里我们采用前缀和思想


ll getnum(int l, int r) {
    return (hash[r]-hash[l-1]*pw[r-l+1]);//对齐之后以便于相减
}
//=============================
for (int len = 1; len <= n; len++) {
    hash[len]=hash[len - 1]*26+s[len - 1];
}//

pw[0] = 1;
for (int i = 1; i <= 100005; i++) {
    pw[i] = pw[i - 1] * 26;
}//初始化
cout<<get(l,r);

三,例题

1.模板例题

题目描述
对于一个字符串,把字符串的每个字符转化成其对应的数字,可以得到一个128进制的数(每一位上的数字都在0~127中),再将这个128进制的数转成对应的十进制,就可以得到对应的哈希值。

输入一个字符串,请输出其哈希值对998244353取余的结果。

输入描述
输入一行,包含一个字符串。

输出描述
输出其哈希值对998244353取余的结果。

输入样例1
apple
输出样例1
320616011
输入样例2
nihao!chilema?
输出样例2
979475980
数据范围
字符串长度不超过100000

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define ll int
const ll mod=998244353;
ll hashp(string s){
	long long an=0;
	for(int i=0;i<s.size();i++){
		an=(an*128%mod+1ll*s[i])%mod;
	}
	return an%mod;
}

signed main(){
	string s;
	getline(cin,s);
	cout<<hashp(s)<<endl;
		
	return 0;
} 

二。进阶提升

在这里插入图片描述
这里我们使用map来处理密码表之间的对应关系

AC代码:

#include <bits/stdc++.h>
using namespace std;
#define int ll
#define ll long long
const ll mod = 998244353;
ll pw[100005];
unordered_map<char, char> mp;
ll hash_mi[100005], hash_ming[100005];

ll hashp(string s) {
    long long an = 0;
    for (int i = 0; i < s.size(); i++) {
        an = (an * 128 + 1ll * s[i]);
    }
    return an % mod;
}
ll getnum(int l, int r) {
    return (hash_mi[r] - hash_mi[l - 1] * pw[r - l + 1]);
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int T;
    cin >> T;
    pw[0] = 1;
    for (int i = 1; i <= 100005; i++) {
        pw[i] = pw[i - 1] * 26;
    }
    for (int u = 1; u <= T; u++) {
        for (int i = 'a'; i <= 'z'; i++) {
            char a;
            cin >> a;
            mp[a] = i;
        }
        string s;
        cin >> s;
        int n = s.size();

        for (int len = 1; len <= n; len++) {
            hash_mi[len] = hash_mi[len - 1] * 26 + s[len - 1];
            hash_ming[len] = hash_ming[len - 1] * 26 + mp[s[len - 1]];
        }
        int ans = 0;
        for (int k = (n + 1) / 2; k <= n; k++) {
            if (hash_ming[n - k] == getnum(k + 1, n)) {
                ans = k;
                break;
            }
        }
        for (int i = 0; i < ans; i++) {
            cout << s[i];
        }
        for (int i = 0; i < ans; i++) {
            cout << mp[s[i]];
        }
        cout << endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值